This is page 5 of 10. Use http://codebase.md/jakedismo/master-mcp-server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .env.example
├── .eslintignore
├── .eslintrc.cjs
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .prettierrc.json
├── CHANGELOG.md
├── config
│ ├── default.json
│ ├── development.json
│ ├── production.json
│ └── schema.json
├── config.json
├── CONTRIBUTING.md
├── debug-stdio.cjs
├── debug-stdio.js
├── deploy
│ ├── cloudflare
│ │ ├── .gitkeep
│ │ ├── README.md
│ │ └── wrangler.toml
│ ├── docker
│ │ ├── .gitkeep
│ │ ├── docker-compose.yml
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
│ ├── koyeb
│ │ ├── .gitkeep
│ │ └── koyeb.yaml
│ └── README.md
├── docker-compose.yml
├── Dockerfile
├── docs
│ ├── .DS_Store
│ ├── .vitepress
│ │ ├── cache
│ │ │ └── deps
│ │ │ ├── _metadata.json
│ │ │ ├── chunk-HVR2FF6M.js
│ │ │ ├── chunk-HVR2FF6M.js.map
│ │ │ ├── chunk-P2XGSYO7.js
│ │ │ ├── chunk-P2XGSYO7.js.map
│ │ │ ├── package.json
│ │ │ ├── vitepress___@vue_devtools-api.js
│ │ │ ├── vitepress___@vue_devtools-api.js.map
│ │ │ ├── vitepress___@vueuse_core.js
│ │ │ ├── vitepress___@vueuse_core.js.map
│ │ │ ├── vitepress___@vueuse_integrations_useFocusTrap.js
│ │ │ ├── vitepress___@vueuse_integrations_useFocusTrap.js.map
│ │ │ ├── vitepress___mark__js_src_vanilla__js.js
│ │ │ ├── vitepress___mark__js_src_vanilla__js.js.map
│ │ │ ├── vitepress___minisearch.js
│ │ │ ├── vitepress___minisearch.js.map
│ │ │ ├── vue.js
│ │ │ └── vue.js.map
│ │ ├── config.ts
│ │ ├── dist
│ │ │ ├── 404.html
│ │ │ ├── advanced
│ │ │ │ ├── extensibility.html
│ │ │ │ ├── index.html
│ │ │ │ ├── monitoring.html
│ │ │ │ ├── performance.html
│ │ │ │ └── security.html
│ │ │ ├── api
│ │ │ │ ├── index.html
│ │ │ │ └── README.html
│ │ │ ├── assets
│ │ │ │ ├── advanced_extensibility.md.TrXUn5w5.js
│ │ │ │ ├── advanced_extensibility.md.TrXUn5w5.lean.js
│ │ │ │ ├── advanced_index.md.CPcpUlw_.js
│ │ │ │ ├── advanced_index.md.CPcpUlw_.lean.js
│ │ │ │ ├── advanced_monitoring.md.DTybdNg-.js
│ │ │ │ ├── advanced_monitoring.md.DTybdNg-.lean.js
│ │ │ │ ├── advanced_performance.md.DKmzK0ia.js
│ │ │ │ ├── advanced_performance.md.DKmzK0ia.lean.js
│ │ │ │ ├── advanced_security.md.B-oBD7IB.js
│ │ │ │ ├── advanced_security.md.B-oBD7IB.lean.js
│ │ │ │ ├── api_index.md.Dl1JB08_.js
│ │ │ │ ├── api_index.md.Dl1JB08_.lean.js
│ │ │ │ ├── chunks
│ │ │ │ │ └── framework.CHl2ywxc.js
│ │ │ │ ├── configuration_environment-variables.md.Ddy3P_Wz.js
│ │ │ │ ├── configuration_environment-variables.md.Ddy3P_Wz.lean.js
│ │ │ │ ├── configuration_environment.md.DxcTQ623.js
│ │ │ │ ├── configuration_environment.md.DxcTQ623.lean.js
│ │ │ │ ├── configuration_overview.md.DIkVDv7V.js
│ │ │ │ ├── configuration_overview.md.DIkVDv7V.lean.js
│ │ │ │ ├── configuration_performance.md.DbJdmLrW.js
│ │ │ │ ├── configuration_performance.md.DbJdmLrW.lean.js
│ │ │ │ ├── configuration_reference.md.27IKWqtk.js
│ │ │ │ ├── configuration_reference.md.27IKWqtk.lean.js
│ │ │ │ ├── configuration_security.md.-OOlkzN4.js
│ │ │ │ ├── configuration_security.md.-OOlkzN4.lean.js
│ │ │ │ ├── contributing_dev-setup.md.Ceqh4w-R.js
│ │ │ │ ├── contributing_dev-setup.md.Ceqh4w-R.lean.js
│ │ │ │ ├── contributing_guidelines.md.ZEAX2yVh.js
│ │ │ │ ├── contributing_guidelines.md.ZEAX2yVh.lean.js
│ │ │ │ ├── contributing_index.md.DYq9R6wr.js
│ │ │ │ ├── contributing_index.md.DYq9R6wr.lean.js
│ │ │ │ ├── contributing_maintenance.md.k2bR0IaR.js
│ │ │ │ ├── contributing_maintenance.md.k2bR0IaR.lean.js
│ │ │ │ ├── deployment_cicd.md.Ci2T0UYC.js
│ │ │ │ ├── deployment_cicd.md.Ci2T0UYC.lean.js
│ │ │ │ ├── deployment_cloudflare-workers.md.D2WHsfep.js
│ │ │ │ ├── deployment_cloudflare-workers.md.D2WHsfep.lean.js
│ │ │ │ ├── deployment_docker.md.B8bQDQTo.js
│ │ │ │ ├── deployment_docker.md.B8bQDQTo.lean.js
│ │ │ │ ├── deployment_index.md.ClYeOkpy.js
│ │ │ │ ├── deployment_index.md.ClYeOkpy.lean.js
│ │ │ │ ├── deployment_koyeb.md.B_wJhvF7.js
│ │ │ │ ├── deployment_koyeb.md.B_wJhvF7.lean.js
│ │ │ │ ├── examples_advanced-routing.md.B3CqhLZ7.js
│ │ │ │ ├── examples_advanced-routing.md.B3CqhLZ7.lean.js
│ │ │ │ ├── examples_basic-node.md.CaDZzGlO.js
│ │ │ │ ├── examples_basic-node.md.CaDZzGlO.lean.js
│ │ │ │ ├── examples_cloudflare-worker.md.DwVSz-c7.js
│ │ │ │ ├── examples_cloudflare-worker.md.DwVSz-c7.lean.js
│ │ │ │ ├── examples_index.md.CBF_BLkl.js
│ │ │ │ ├── examples_index.md.CBF_BLkl.lean.js
│ │ │ │ ├── examples_oauth-delegation.md.1hZxoqDl.js
│ │ │ │ ├── examples_oauth-delegation.md.1hZxoqDl.lean.js
│ │ │ │ ├── examples_overview.md.CZN0JbZ7.js
│ │ │ │ ├── examples_overview.md.CZN0JbZ7.lean.js
│ │ │ │ ├── examples_testing.md.Dek4GpNs.js
│ │ │ │ ├── examples_testing.md.Dek4GpNs.lean.js
│ │ │ │ ├── getting-started_concepts.md.D7ON9iGB.js
│ │ │ │ ├── getting-started_concepts.md.D7ON9iGB.lean.js
│ │ │ │ ├── getting-started_installation.md.BKnVqAGg.js
│ │ │ │ ├── getting-started_installation.md.BKnVqAGg.lean.js
│ │ │ │ ├── getting-started_overview.md.DvJDFL2N.js
│ │ │ │ ├── getting-started_overview.md.DvJDFL2N.lean.js
│ │ │ │ ├── getting-started_quickstart-node.md.GOO4aGas.js
│ │ │ │ ├── getting-started_quickstart-node.md.GOO4aGas.lean.js
│ │ │ │ ├── getting-started_quickstart-workers.md.Cpofh8Mj.js
│ │ │ │ ├── getting-started_quickstart-workers.md.Cpofh8Mj.lean.js
│ │ │ │ ├── getting-started.md.DG9ndneo.js
│ │ │ │ ├── getting-started.md.DG9ndneo.lean.js
│ │ │ │ ├── guides_configuration-management.md.B-jwYMbA.js
│ │ │ │ ├── guides_configuration-management.md.B-jwYMbA.lean.js
│ │ │ │ ├── guides_configuration.md.Ci3zYDFA.js
│ │ │ │ ├── guides_configuration.md.Ci3zYDFA.lean.js
│ │ │ │ ├── guides_index.md.CIlq2fmx.js
│ │ │ │ ├── guides_index.md.CIlq2fmx.lean.js
│ │ │ │ ├── guides_module-loading.md.BkJvuRnQ.js
│ │ │ │ ├── guides_module-loading.md.BkJvuRnQ.lean.js
│ │ │ │ ├── guides_oauth-delegation.md.DEOZ-_G0.js
│ │ │ │ ├── guides_oauth-delegation.md.DEOZ-_G0.lean.js
│ │ │ │ ├── guides_request-routing.md.Bdzf0VLg.js
│ │ │ │ ├── guides_request-routing.md.Bdzf0VLg.lean.js
│ │ │ │ ├── guides_testing.md.kYfHqJLu.js
│ │ │ │ ├── guides_testing.md.kYfHqJLu.lean.js
│ │ │ │ ├── inter-italic-cyrillic-ext.r48I6akx.woff2
│ │ │ │ ├── inter-italic-cyrillic.By2_1cv3.woff2
│ │ │ │ ├── inter-italic-greek-ext.1u6EdAuj.woff2
│ │ │ │ ├── inter-italic-greek.DJ8dCoTZ.woff2
│ │ │ │ ├── inter-italic-latin-ext.CN1xVJS-.woff2
│ │ │ │ ├── inter-italic-latin.C2AdPX0b.woff2
│ │ │ │ ├── inter-italic-vietnamese.BSbpV94h.woff2
│ │ │ │ ├── inter-roman-cyrillic-ext.BBPuwvHQ.woff2
│ │ │ │ ├── inter-roman-cyrillic.C5lxZ8CY.woff2
│ │ │ │ ├── inter-roman-greek-ext.CqjqNYQ-.woff2
│ │ │ │ ├── inter-roman-greek.BBVDIX6e.woff2
│ │ │ │ ├── inter-roman-latin-ext.4ZJIpNVo.woff2
│ │ │ │ ├── inter-roman-latin.Di8DUHzh.woff2
│ │ │ │ ├── inter-roman-vietnamese.BjW4sHH5.woff2
│ │ │ │ ├── README.md.BO5r5M9u.js
│ │ │ │ ├── README.md.BO5r5M9u.lean.js
│ │ │ │ ├── style.BQrfSMzK.css
│ │ │ │ ├── troubleshooting_common-issues.md.CScvzWM1.js
│ │ │ │ ├── troubleshooting_common-issues.md.CScvzWM1.lean.js
│ │ │ │ ├── troubleshooting_deployment.md.DUhpqnLE.js
│ │ │ │ ├── troubleshooting_deployment.md.DUhpqnLE.lean.js
│ │ │ │ ├── troubleshooting_errors.md.BSCsEmGc.js
│ │ │ │ ├── troubleshooting_errors.md.BSCsEmGc.lean.js
│ │ │ │ ├── troubleshooting_oauth.md.Cw60Eka3.js
│ │ │ │ ├── troubleshooting_oauth.md.Cw60Eka3.lean.js
│ │ │ │ ├── troubleshooting_performance.md.DxY6LJcT.js
│ │ │ │ ├── troubleshooting_performance.md.DxY6LJcT.lean.js
│ │ │ │ ├── troubleshooting_routing.md.BHN-MDhs.js
│ │ │ │ ├── troubleshooting_routing.md.BHN-MDhs.lean.js
│ │ │ │ ├── troubleshooting_security-best-practices.md.Yiu8E-zt.js
│ │ │ │ ├── troubleshooting_security-best-practices.md.Yiu8E-zt.lean.js
│ │ │ │ ├── tutorials_beginner-getting-started.md.BXObgobW.js
│ │ │ │ ├── tutorials_beginner-getting-started.md.BXObgobW.lean.js
│ │ │ │ ├── tutorials_cloudflare-workers-tutorial.md.MPHsc0aT.js
│ │ │ │ ├── tutorials_cloudflare-workers-tutorial.md.MPHsc0aT.lean.js
│ │ │ │ ├── tutorials_load-balancing-and-resilience.md.Dv9r9jyW.js
│ │ │ │ ├── tutorials_load-balancing-and-resilience.md.Dv9r9jyW.lean.js
│ │ │ │ ├── tutorials_oauth-delegation-github.md.Nq4glqCe.js
│ │ │ │ └── tutorials_oauth-delegation-github.md.Nq4glqCe.lean.js
│ │ │ ├── configuration
│ │ │ │ ├── environment-variables.html
│ │ │ │ ├── environment.html
│ │ │ │ ├── examples.html
│ │ │ │ ├── overview.html
│ │ │ │ ├── performance.html
│ │ │ │ ├── reference.html
│ │ │ │ └── security.html
│ │ │ ├── contributing
│ │ │ │ ├── dev-setup.html
│ │ │ │ ├── guidelines.html
│ │ │ │ ├── index.html
│ │ │ │ └── maintenance.html
│ │ │ ├── deployment
│ │ │ │ ├── cicd.html
│ │ │ │ ├── cloudflare-workers.html
│ │ │ │ ├── docker.html
│ │ │ │ ├── index.html
│ │ │ │ └── koyeb.html
│ │ │ ├── diagrams
│ │ │ │ └── architecture.svg
│ │ │ ├── examples
│ │ │ │ ├── advanced-routing.html
│ │ │ │ ├── basic-node.html
│ │ │ │ ├── cloudflare-worker.html
│ │ │ │ ├── index.html
│ │ │ │ ├── oauth-delegation.html
│ │ │ │ ├── overview.html
│ │ │ │ └── testing.html
│ │ │ ├── getting-started
│ │ │ │ ├── concepts.html
│ │ │ │ ├── installation.html
│ │ │ │ ├── overview.html
│ │ │ │ ├── quick-start.html
│ │ │ │ ├── quickstart-node.html
│ │ │ │ └── quickstart-workers.html
│ │ │ ├── getting-started.html
│ │ │ ├── guides
│ │ │ │ ├── authentication.html
│ │ │ │ ├── client-integration.html
│ │ │ │ ├── configuration-management.html
│ │ │ │ ├── configuration.html
│ │ │ │ ├── index.html
│ │ │ │ ├── module-loading.html
│ │ │ │ ├── oauth-delegation.html
│ │ │ │ ├── request-routing.html
│ │ │ │ ├── server-management.html
│ │ │ │ ├── server-sharing.html
│ │ │ │ └── testing.html
│ │ │ ├── hashmap.json
│ │ │ ├── index.html
│ │ │ ├── logo.svg
│ │ │ ├── README.html
│ │ │ ├── reports
│ │ │ │ └── mcp-compliance-audit.html
│ │ │ ├── troubleshooting
│ │ │ │ ├── common-issues.html
│ │ │ │ ├── deployment.html
│ │ │ │ ├── errors.html
│ │ │ │ ├── index.html
│ │ │ │ ├── oauth.html
│ │ │ │ ├── performance.html
│ │ │ │ ├── routing.html
│ │ │ │ └── security-best-practices.html
│ │ │ ├── tutorials
│ │ │ │ ├── beginner-getting-started.html
│ │ │ │ ├── cloudflare-workers-tutorial.html
│ │ │ │ ├── load-balancing-and-resilience.html
│ │ │ │ └── oauth-delegation-github.html
│ │ │ └── vp-icons.css
│ │ └── theme
│ │ ├── components
│ │ │ ├── ApiPlayground.vue
│ │ │ ├── AuthFlowDemo.vue
│ │ │ ├── CodeTabs.vue
│ │ │ └── ConfigGenerator.vue
│ │ ├── index.ts
│ │ └── style.css
│ ├── advanced
│ │ ├── extensibility.md
│ │ ├── index.md
│ │ ├── monitoring.md
│ │ ├── performance.md
│ │ └── security.md
│ ├── api
│ │ ├── functions
│ │ │ └── createServer.md
│ │ ├── index.md
│ │ ├── interfaces
│ │ │ └── RunningServer.md
│ │ └── README.md
│ ├── architecture
│ │ └── images
│ │ └── mcp_master_architecture.svg
│ ├── configuration
│ │ ├── environment-variables.md
│ │ ├── environment.md
│ │ ├── examples.md
│ │ ├── overview.md
│ │ ├── performance.md
│ │ ├── reference.md
│ │ └── security.md
│ ├── contributing
│ │ ├── dev-setup.md
│ │ ├── guidelines.md
│ │ ├── index.md
│ │ └── maintenance.md
│ ├── deployment
│ │ ├── cicd.md
│ │ ├── cloudflare-workers.md
│ │ ├── docker.md
│ │ ├── docs-site.md
│ │ ├── index.md
│ │ └── koyeb.md
│ ├── examples
│ │ ├── advanced-routing.md
│ │ ├── basic-node.md
│ │ ├── cloudflare-worker.md
│ │ ├── index.md
│ │ ├── oauth-delegation.md
│ │ ├── overview.md
│ │ └── testing.md
│ ├── getting-started
│ │ ├── concepts.md
│ │ ├── installation.md
│ │ ├── overview.md
│ │ ├── quick-start.md
│ │ ├── quickstart-node.md
│ │ └── quickstart-workers.md
│ ├── getting-started.md
│ ├── guides
│ │ ├── authentication.md
│ │ ├── client-integration.md
│ │ ├── configuration-management.md
│ │ ├── configuration.md
│ │ ├── index.md
│ │ ├── module-loading.md
│ │ ├── oauth-delegation.md
│ │ ├── request-routing.md
│ │ ├── server-management.md
│ │ ├── server-sharing.md
│ │ └── testing.md
│ ├── index.html
│ ├── public
│ │ ├── diagrams
│ │ │ └── architecture.svg
│ │ ├── github-social.png
│ │ │ └── image.png
│ │ ├── logo.png
│ │ └── logo.svg
│ ├── README.md
│ ├── stdio-servers.md
│ ├── testing
│ │ └── phase-9-testing-architecture.md
│ ├── troubleshooting
│ │ ├── common-issues.md
│ │ ├── deployment.md
│ │ ├── errors.md
│ │ ├── index.md
│ │ ├── oauth.md
│ │ ├── performance.md
│ │ ├── routing.md
│ │ └── security-best-practices.md
│ └── tutorials
│ ├── beginner-getting-started.md
│ ├── cloudflare-workers-tutorial.md
│ ├── load-balancing-and-resilience.md
│ └── oauth-delegation-github.md
├── examples
│ ├── advanced-routing
│ │ ├── config.yaml
│ │ └── README.md
│ ├── basic-node
│ │ ├── config.yaml
│ │ ├── README.md
│ │ └── server.ts
│ ├── cloudflare-worker
│ │ ├── README.md
│ │ └── worker.ts
│ ├── custom-auth
│ │ ├── config.yaml
│ │ ├── index.ts
│ │ └── README.md
│ ├── multi-server
│ │ ├── config.yaml
│ │ └── README.md
│ ├── oauth-delegation
│ │ └── README.md
│ ├── oauth-node
│ │ ├── config.yaml
│ │ └── README.md
│ ├── performance
│ │ ├── config.yaml
│ │ └── README.md
│ ├── sample-configs
│ │ ├── basic.yaml
│ │ └── simple-setup.yaml
│ ├── security-hardening
│ │ └── README.md
│ ├── stdio-mcp-server.cjs
│ ├── test-mcp-server.js
│ └── test-stdio-server.js
├── LICENSE
├── master-mcp-definition.md
├── package-lock.json
├── package.json
├── README.md
├── reports
│ └── claude_report_20250815_222153.html
├── scripts
│ └── generate-config-docs.ts
├── src
│ ├── auth
│ │ ├── multi-auth-manager.ts
│ │ ├── oauth-providers.ts
│ │ └── token-manager.ts
│ ├── config
│ │ ├── config-loader.ts
│ │ ├── environment-manager.ts
│ │ ├── schema-validator.ts
│ │ └── secret-manager.ts
│ ├── index.ts
│ ├── mcp-server.ts
│ ├── modules
│ │ ├── capability-aggregator.ts
│ │ ├── module-loader.ts
│ │ ├── request-router.ts
│ │ ├── stdio-capability-discovery.ts
│ │ └── stdio-manager.ts
│ ├── oauth
│ │ ├── callback-handler.ts
│ │ ├── flow-controller.ts
│ │ ├── flow-validator.ts
│ │ ├── pkce-manager.ts
│ │ ├── state-manager.ts
│ │ └── web-interface.ts
│ ├── routing
│ │ ├── circuit-breaker.ts
│ │ ├── load-balancer.ts
│ │ ├── retry-handler.ts
│ │ └── route-registry.ts
│ ├── runtime
│ │ ├── node.ts
│ │ └── worker.ts
│ ├── server
│ │ ├── config-manager.ts
│ │ ├── dependency-container.ts
│ │ ├── master-server.ts
│ │ └── protocol-handler.ts
│ ├── types
│ │ ├── auth.ts
│ │ ├── config.ts
│ │ ├── jose-shim.d.ts
│ │ ├── mcp.ts
│ │ └── server.ts
│ └── utils
│ ├── cache.ts
│ ├── crypto.ts
│ ├── dev.ts
│ ├── errors.ts
│ ├── http.ts
│ ├── logger.ts
│ ├── monitoring.ts
│ ├── string.ts
│ ├── time.ts
│ ├── validation.ts
│ └── validators.ts
├── static
│ └── oauth
│ ├── consent.html
│ ├── error.html
│ ├── script.js
│ ├── style.css
│ └── success.html
├── tests
│ ├── _setup
│ │ ├── miniflare.setup.ts
│ │ └── vitest.setup.ts
│ ├── _utils
│ │ ├── log-capture.ts
│ │ ├── mock-fetch.ts
│ │ └── test-server.ts
│ ├── .gitkeep
│ ├── e2e
│ │ ├── flow-controller.express.test.ts
│ │ └── flow-controller.worker.test.ts
│ ├── factories
│ │ ├── configFactory.ts
│ │ ├── mcpFactory.ts
│ │ └── oauthFactory.ts
│ ├── fixtures
│ │ ├── capabilities.json
│ │ └── stdio-server.js
│ ├── integration
│ │ ├── modules.capability-aggregator.test.ts
│ │ ├── modules.module-loader-health.test.ts
│ │ ├── oauth.callback-handler.test.ts
│ │ └── request-router.test.ts
│ ├── mocks
│ │ ├── mcp
│ │ │ └── fake-backend.ts
│ │ └── oauth
│ │ └── mock-oidc-provider.ts
│ ├── perf
│ │ ├── artillery
│ │ │ └── auth-routing.yaml
│ │ └── perf.auth-and-routing.test.ts
│ ├── security
│ │ └── security.oauth-and-input.test.ts
│ ├── servers
│ │ ├── test-auth-simple.js
│ │ ├── test-debug.js
│ │ ├── test-master-mcp.js
│ │ ├── test-mcp-client.js
│ │ ├── test-streaming-both-complete.js
│ │ ├── test-streaming-both-full.js
│ │ ├── test-streaming-both-simple.js
│ │ ├── test-streaming-both.js
│ │ └── test-streaming.js
│ ├── setup
│ │ └── test-setup.ts
│ ├── unit
│ │ ├── auth.multi-auth-manager.test.ts
│ │ ├── auth.token-manager.test.ts
│ │ ├── config.environment-manager.test.ts
│ │ ├── config.schema-validator.test.ts
│ │ ├── config.secret-manager.test.ts
│ │ ├── modules
│ │ │ ├── stdio-capability-discovery.test.ts
│ │ │ └── stdio-manager.test.ts
│ │ ├── modules.route-registry.test.ts
│ │ ├── oauth.pkce-state.test.ts
│ │ ├── routing
│ │ │ └── circuit-breaker.test.ts
│ │ ├── routing.core.test.ts
│ │ ├── stdio-capability-discovery.test.ts
│ │ ├── utils.crypto.test.ts
│ │ ├── utils.logger.test.ts
│ │ └── utils.monitoring.test.ts
│ └── utils
│ ├── fake-express.ts
│ ├── mock-http.ts
│ ├── oauth-mocks.ts
│ └── token-storages.ts
├── tsconfig.base.json
├── tsconfig.json
├── tsconfig.node.json
├── tsconfig.worker.json
├── typedoc.json
├── vitest.config.ts
└── vitest.worker.config.ts
```
# Files
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js:
--------------------------------------------------------------------------------
```javascript
1 | import {
2 | notNullish,
3 | toArray,
4 | tryOnScopeDispose,
5 | unrefElement
6 | } from "./chunk-P2XGSYO7.js";
7 | import {
8 | computed,
9 | shallowRef,
10 | toValue,
11 | watch
12 | } from "./chunk-HVR2FF6M.js";
13 |
14 | // node_modules/tabbable/dist/index.esm.js
15 | var candidateSelectors = ["input:not([inert])", "select:not([inert])", "textarea:not([inert])", "a[href]:not([inert])", "button:not([inert])", "[tabindex]:not(slot):not([inert])", "audio[controls]:not([inert])", "video[controls]:not([inert])", '[contenteditable]:not([contenteditable="false"]):not([inert])', "details>summary:first-of-type:not([inert])", "details:not([inert])"];
16 | var candidateSelector = candidateSelectors.join(",");
17 | var NoElement = typeof Element === "undefined";
18 | var matches = NoElement ? function() {
19 | } : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
20 | var getRootNode = !NoElement && Element.prototype.getRootNode ? function(element) {
21 | var _element$getRootNode;
22 | return element === null || element === void 0 ? void 0 : (_element$getRootNode = element.getRootNode) === null || _element$getRootNode === void 0 ? void 0 : _element$getRootNode.call(element);
23 | } : function(element) {
24 | return element === null || element === void 0 ? void 0 : element.ownerDocument;
25 | };
26 | var isInert = function isInert2(node, lookUp) {
27 | var _node$getAttribute;
28 | if (lookUp === void 0) {
29 | lookUp = true;
30 | }
31 | var inertAtt = node === null || node === void 0 ? void 0 : (_node$getAttribute = node.getAttribute) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.call(node, "inert");
32 | var inert = inertAtt === "" || inertAtt === "true";
33 | var result = inert || lookUp && node && isInert2(node.parentNode);
34 | return result;
35 | };
36 | var isContentEditable = function isContentEditable2(node) {
37 | var _node$getAttribute2;
38 | var attValue = node === null || node === void 0 ? void 0 : (_node$getAttribute2 = node.getAttribute) === null || _node$getAttribute2 === void 0 ? void 0 : _node$getAttribute2.call(node, "contenteditable");
39 | return attValue === "" || attValue === "true";
40 | };
41 | var getCandidates = function getCandidates2(el, includeContainer, filter) {
42 | if (isInert(el)) {
43 | return [];
44 | }
45 | var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector));
46 | if (includeContainer && matches.call(el, candidateSelector)) {
47 | candidates.unshift(el);
48 | }
49 | candidates = candidates.filter(filter);
50 | return candidates;
51 | };
52 | var getCandidatesIteratively = function getCandidatesIteratively2(elements, includeContainer, options) {
53 | var candidates = [];
54 | var elementsToCheck = Array.from(elements);
55 | while (elementsToCheck.length) {
56 | var element = elementsToCheck.shift();
57 | if (isInert(element, false)) {
58 | continue;
59 | }
60 | if (element.tagName === "SLOT") {
61 | var assigned = element.assignedElements();
62 | var content = assigned.length ? assigned : element.children;
63 | var nestedCandidates = getCandidatesIteratively2(content, true, options);
64 | if (options.flatten) {
65 | candidates.push.apply(candidates, nestedCandidates);
66 | } else {
67 | candidates.push({
68 | scopeParent: element,
69 | candidates: nestedCandidates
70 | });
71 | }
72 | } else {
73 | var validCandidate = matches.call(element, candidateSelector);
74 | if (validCandidate && options.filter(element) && (includeContainer || !elements.includes(element))) {
75 | candidates.push(element);
76 | }
77 | var shadowRoot = element.shadowRoot || // check for an undisclosed shadow
78 | typeof options.getShadowRoot === "function" && options.getShadowRoot(element);
79 | var validShadowRoot = !isInert(shadowRoot, false) && (!options.shadowRootFilter || options.shadowRootFilter(element));
80 | if (shadowRoot && validShadowRoot) {
81 | var _nestedCandidates = getCandidatesIteratively2(shadowRoot === true ? element.children : shadowRoot.children, true, options);
82 | if (options.flatten) {
83 | candidates.push.apply(candidates, _nestedCandidates);
84 | } else {
85 | candidates.push({
86 | scopeParent: element,
87 | candidates: _nestedCandidates
88 | });
89 | }
90 | } else {
91 | elementsToCheck.unshift.apply(elementsToCheck, element.children);
92 | }
93 | }
94 | }
95 | return candidates;
96 | };
97 | var hasTabIndex = function hasTabIndex2(node) {
98 | return !isNaN(parseInt(node.getAttribute("tabindex"), 10));
99 | };
100 | var getTabIndex = function getTabIndex2(node) {
101 | if (!node) {
102 | throw new Error("No node provided");
103 | }
104 | if (node.tabIndex < 0) {
105 | if ((/^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || isContentEditable(node)) && !hasTabIndex(node)) {
106 | return 0;
107 | }
108 | }
109 | return node.tabIndex;
110 | };
111 | var getSortOrderTabIndex = function getSortOrderTabIndex2(node, isScope) {
112 | var tabIndex = getTabIndex(node);
113 | if (tabIndex < 0 && isScope && !hasTabIndex(node)) {
114 | return 0;
115 | }
116 | return tabIndex;
117 | };
118 | var sortOrderedTabbables = function sortOrderedTabbables2(a, b) {
119 | return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;
120 | };
121 | var isInput = function isInput2(node) {
122 | return node.tagName === "INPUT";
123 | };
124 | var isHiddenInput = function isHiddenInput2(node) {
125 | return isInput(node) && node.type === "hidden";
126 | };
127 | var isDetailsWithSummary = function isDetailsWithSummary2(node) {
128 | var r = node.tagName === "DETAILS" && Array.prototype.slice.apply(node.children).some(function(child) {
129 | return child.tagName === "SUMMARY";
130 | });
131 | return r;
132 | };
133 | var getCheckedRadio = function getCheckedRadio2(nodes, form) {
134 | for (var i = 0; i < nodes.length; i++) {
135 | if (nodes[i].checked && nodes[i].form === form) {
136 | return nodes[i];
137 | }
138 | }
139 | };
140 | var isTabbableRadio = function isTabbableRadio2(node) {
141 | if (!node.name) {
142 | return true;
143 | }
144 | var radioScope = node.form || getRootNode(node);
145 | var queryRadios = function queryRadios2(name) {
146 | return radioScope.querySelectorAll('input[type="radio"][name="' + name + '"]');
147 | };
148 | var radioSet;
149 | if (typeof window !== "undefined" && typeof window.CSS !== "undefined" && typeof window.CSS.escape === "function") {
150 | radioSet = queryRadios(window.CSS.escape(node.name));
151 | } else {
152 | try {
153 | radioSet = queryRadios(node.name);
154 | } catch (err) {
155 | console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s", err.message);
156 | return false;
157 | }
158 | }
159 | var checked = getCheckedRadio(radioSet, node.form);
160 | return !checked || checked === node;
161 | };
162 | var isRadio = function isRadio2(node) {
163 | return isInput(node) && node.type === "radio";
164 | };
165 | var isNonTabbableRadio = function isNonTabbableRadio2(node) {
166 | return isRadio(node) && !isTabbableRadio(node);
167 | };
168 | var isNodeAttached = function isNodeAttached2(node) {
169 | var _nodeRoot;
170 | var nodeRoot = node && getRootNode(node);
171 | var nodeRootHost = (_nodeRoot = nodeRoot) === null || _nodeRoot === void 0 ? void 0 : _nodeRoot.host;
172 | var attached = false;
173 | if (nodeRoot && nodeRoot !== node) {
174 | var _nodeRootHost, _nodeRootHost$ownerDo, _node$ownerDocument;
175 | attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && (_nodeRootHost$ownerDo = _nodeRootHost.ownerDocument) !== null && _nodeRootHost$ownerDo !== void 0 && _nodeRootHost$ownerDo.contains(nodeRootHost) || node !== null && node !== void 0 && (_node$ownerDocument = node.ownerDocument) !== null && _node$ownerDocument !== void 0 && _node$ownerDocument.contains(node));
176 | while (!attached && nodeRootHost) {
177 | var _nodeRoot2, _nodeRootHost2, _nodeRootHost2$ownerD;
178 | nodeRoot = getRootNode(nodeRootHost);
179 | nodeRootHost = (_nodeRoot2 = nodeRoot) === null || _nodeRoot2 === void 0 ? void 0 : _nodeRoot2.host;
180 | attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && (_nodeRootHost2$ownerD = _nodeRootHost2.ownerDocument) !== null && _nodeRootHost2$ownerD !== void 0 && _nodeRootHost2$ownerD.contains(nodeRootHost));
181 | }
182 | }
183 | return attached;
184 | };
185 | var isZeroArea = function isZeroArea2(node) {
186 | var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;
187 | return width === 0 && height === 0;
188 | };
189 | var isHidden = function isHidden2(node, _ref) {
190 | var displayCheck = _ref.displayCheck, getShadowRoot = _ref.getShadowRoot;
191 | if (getComputedStyle(node).visibility === "hidden") {
192 | return true;
193 | }
194 | var isDirectSummary = matches.call(node, "details>summary:first-of-type");
195 | var nodeUnderDetails = isDirectSummary ? node.parentElement : node;
196 | if (matches.call(nodeUnderDetails, "details:not([open]) *")) {
197 | return true;
198 | }
199 | if (!displayCheck || displayCheck === "full" || displayCheck === "legacy-full") {
200 | if (typeof getShadowRoot === "function") {
201 | var originalNode = node;
202 | while (node) {
203 | var parentElement = node.parentElement;
204 | var rootNode = getRootNode(node);
205 | if (parentElement && !parentElement.shadowRoot && getShadowRoot(parentElement) === true) {
206 | return isZeroArea(node);
207 | } else if (node.assignedSlot) {
208 | node = node.assignedSlot;
209 | } else if (!parentElement && rootNode !== node.ownerDocument) {
210 | node = rootNode.host;
211 | } else {
212 | node = parentElement;
213 | }
214 | }
215 | node = originalNode;
216 | }
217 | if (isNodeAttached(node)) {
218 | return !node.getClientRects().length;
219 | }
220 | if (displayCheck !== "legacy-full") {
221 | return true;
222 | }
223 | } else if (displayCheck === "non-zero-area") {
224 | return isZeroArea(node);
225 | }
226 | return false;
227 | };
228 | var isDisabledFromFieldset = function isDisabledFromFieldset2(node) {
229 | if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(node.tagName)) {
230 | var parentNode = node.parentElement;
231 | while (parentNode) {
232 | if (parentNode.tagName === "FIELDSET" && parentNode.disabled) {
233 | for (var i = 0; i < parentNode.children.length; i++) {
234 | var child = parentNode.children.item(i);
235 | if (child.tagName === "LEGEND") {
236 | return matches.call(parentNode, "fieldset[disabled] *") ? true : !child.contains(node);
237 | }
238 | }
239 | return true;
240 | }
241 | parentNode = parentNode.parentElement;
242 | }
243 | }
244 | return false;
245 | };
246 | var isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable2(options, node) {
247 | if (node.disabled || // we must do an inert look up to filter out any elements inside an inert ancestor
248 | // because we're limited in the type of selectors we can use in JSDom (see related
249 | // note related to `candidateSelectors`)
250 | isInert(node) || isHiddenInput(node) || isHidden(node, options) || // For a details element with a summary, the summary element gets the focus
251 | isDetailsWithSummary(node) || isDisabledFromFieldset(node)) {
252 | return false;
253 | }
254 | return true;
255 | };
256 | var isNodeMatchingSelectorTabbable = function isNodeMatchingSelectorTabbable2(options, node) {
257 | if (isNonTabbableRadio(node) || getTabIndex(node) < 0 || !isNodeMatchingSelectorFocusable(options, node)) {
258 | return false;
259 | }
260 | return true;
261 | };
262 | var isValidShadowRootTabbable = function isValidShadowRootTabbable2(shadowHostNode) {
263 | var tabIndex = parseInt(shadowHostNode.getAttribute("tabindex"), 10);
264 | if (isNaN(tabIndex) || tabIndex >= 0) {
265 | return true;
266 | }
267 | return false;
268 | };
269 | var sortByOrder = function sortByOrder2(candidates) {
270 | var regularTabbables = [];
271 | var orderedTabbables = [];
272 | candidates.forEach(function(item, i) {
273 | var isScope = !!item.scopeParent;
274 | var element = isScope ? item.scopeParent : item;
275 | var candidateTabindex = getSortOrderTabIndex(element, isScope);
276 | var elements = isScope ? sortByOrder2(item.candidates) : element;
277 | if (candidateTabindex === 0) {
278 | isScope ? regularTabbables.push.apply(regularTabbables, elements) : regularTabbables.push(element);
279 | } else {
280 | orderedTabbables.push({
281 | documentOrder: i,
282 | tabIndex: candidateTabindex,
283 | item,
284 | isScope,
285 | content: elements
286 | });
287 | }
288 | });
289 | return orderedTabbables.sort(sortOrderedTabbables).reduce(function(acc, sortable) {
290 | sortable.isScope ? acc.push.apply(acc, sortable.content) : acc.push(sortable.content);
291 | return acc;
292 | }, []).concat(regularTabbables);
293 | };
294 | var tabbable = function tabbable2(container, options) {
295 | options = options || {};
296 | var candidates;
297 | if (options.getShadowRoot) {
298 | candidates = getCandidatesIteratively([container], options.includeContainer, {
299 | filter: isNodeMatchingSelectorTabbable.bind(null, options),
300 | flatten: false,
301 | getShadowRoot: options.getShadowRoot,
302 | shadowRootFilter: isValidShadowRootTabbable
303 | });
304 | } else {
305 | candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorTabbable.bind(null, options));
306 | }
307 | return sortByOrder(candidates);
308 | };
309 | var focusable = function focusable2(container, options) {
310 | options = options || {};
311 | var candidates;
312 | if (options.getShadowRoot) {
313 | candidates = getCandidatesIteratively([container], options.includeContainer, {
314 | filter: isNodeMatchingSelectorFocusable.bind(null, options),
315 | flatten: true,
316 | getShadowRoot: options.getShadowRoot
317 | });
318 | } else {
319 | candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorFocusable.bind(null, options));
320 | }
321 | return candidates;
322 | };
323 | var isTabbable = function isTabbable2(node, options) {
324 | options = options || {};
325 | if (!node) {
326 | throw new Error("No node provided");
327 | }
328 | if (matches.call(node, candidateSelector) === false) {
329 | return false;
330 | }
331 | return isNodeMatchingSelectorTabbable(options, node);
332 | };
333 | var focusableCandidateSelector = candidateSelectors.concat("iframe").join(",");
334 | var isFocusable = function isFocusable2(node, options) {
335 | options = options || {};
336 | if (!node) {
337 | throw new Error("No node provided");
338 | }
339 | if (matches.call(node, focusableCandidateSelector) === false) {
340 | return false;
341 | }
342 | return isNodeMatchingSelectorFocusable(options, node);
343 | };
344 |
345 | // node_modules/focus-trap/dist/focus-trap.esm.js
346 | function _arrayLikeToArray(r, a) {
347 | (null == a || a > r.length) && (a = r.length);
348 | for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
349 | return n;
350 | }
351 | function _arrayWithoutHoles(r) {
352 | if (Array.isArray(r)) return _arrayLikeToArray(r);
353 | }
354 | function _defineProperty(e, r, t) {
355 | return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
356 | value: t,
357 | enumerable: true,
358 | configurable: true,
359 | writable: true
360 | }) : e[r] = t, e;
361 | }
362 | function _iterableToArray(r) {
363 | if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
364 | }
365 | function _nonIterableSpread() {
366 | throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
367 | }
368 | function ownKeys(e, r) {
369 | var t = Object.keys(e);
370 | if (Object.getOwnPropertySymbols) {
371 | var o = Object.getOwnPropertySymbols(e);
372 | r && (o = o.filter(function(r2) {
373 | return Object.getOwnPropertyDescriptor(e, r2).enumerable;
374 | })), t.push.apply(t, o);
375 | }
376 | return t;
377 | }
378 | function _objectSpread2(e) {
379 | for (var r = 1; r < arguments.length; r++) {
380 | var t = null != arguments[r] ? arguments[r] : {};
381 | r % 2 ? ownKeys(Object(t), true).forEach(function(r2) {
382 | _defineProperty(e, r2, t[r2]);
383 | }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) {
384 | Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
385 | });
386 | }
387 | return e;
388 | }
389 | function _toConsumableArray(r) {
390 | return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
391 | }
392 | function _toPrimitive(t, r) {
393 | if ("object" != typeof t || !t) return t;
394 | var e = t[Symbol.toPrimitive];
395 | if (void 0 !== e) {
396 | var i = e.call(t, r);
397 | if ("object" != typeof i) return i;
398 | throw new TypeError("@@toPrimitive must return a primitive value.");
399 | }
400 | return ("string" === r ? String : Number)(t);
401 | }
402 | function _toPropertyKey(t) {
403 | var i = _toPrimitive(t, "string");
404 | return "symbol" == typeof i ? i : i + "";
405 | }
406 | function _unsupportedIterableToArray(r, a) {
407 | if (r) {
408 | if ("string" == typeof r) return _arrayLikeToArray(r, a);
409 | var t = {}.toString.call(r).slice(8, -1);
410 | return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
411 | }
412 | }
413 | var activeFocusTraps = {
414 | activateTrap: function activateTrap(trapStack, trap) {
415 | if (trapStack.length > 0) {
416 | var activeTrap = trapStack[trapStack.length - 1];
417 | if (activeTrap !== trap) {
418 | activeTrap._setPausedState(true);
419 | }
420 | }
421 | var trapIndex = trapStack.indexOf(trap);
422 | if (trapIndex === -1) {
423 | trapStack.push(trap);
424 | } else {
425 | trapStack.splice(trapIndex, 1);
426 | trapStack.push(trap);
427 | }
428 | },
429 | deactivateTrap: function deactivateTrap(trapStack, trap) {
430 | var trapIndex = trapStack.indexOf(trap);
431 | if (trapIndex !== -1) {
432 | trapStack.splice(trapIndex, 1);
433 | }
434 | if (trapStack.length > 0 && !trapStack[trapStack.length - 1]._isManuallyPaused()) {
435 | trapStack[trapStack.length - 1]._setPausedState(false);
436 | }
437 | }
438 | };
439 | var isSelectableInput = function isSelectableInput2(node) {
440 | return node.tagName && node.tagName.toLowerCase() === "input" && typeof node.select === "function";
441 | };
442 | var isEscapeEvent = function isEscapeEvent2(e) {
443 | return (e === null || e === void 0 ? void 0 : e.key) === "Escape" || (e === null || e === void 0 ? void 0 : e.key) === "Esc" || (e === null || e === void 0 ? void 0 : e.keyCode) === 27;
444 | };
445 | var isTabEvent = function isTabEvent2(e) {
446 | return (e === null || e === void 0 ? void 0 : e.key) === "Tab" || (e === null || e === void 0 ? void 0 : e.keyCode) === 9;
447 | };
448 | var isKeyForward = function isKeyForward2(e) {
449 | return isTabEvent(e) && !e.shiftKey;
450 | };
451 | var isKeyBackward = function isKeyBackward2(e) {
452 | return isTabEvent(e) && e.shiftKey;
453 | };
454 | var delay = function delay2(fn) {
455 | return setTimeout(fn, 0);
456 | };
457 | var valueOrHandler = function valueOrHandler2(value) {
458 | for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
459 | params[_key - 1] = arguments[_key];
460 | }
461 | return typeof value === "function" ? value.apply(void 0, params) : value;
462 | };
463 | var getActualTarget = function getActualTarget2(event) {
464 | return event.target.shadowRoot && typeof event.composedPath === "function" ? event.composedPath()[0] : event.target;
465 | };
466 | var internalTrapStack = [];
467 | var createFocusTrap = function createFocusTrap2(elements, userOptions) {
468 | var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;
469 | var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;
470 | var config = _objectSpread2({
471 | returnFocusOnDeactivate: true,
472 | escapeDeactivates: true,
473 | delayInitialFocus: true,
474 | isKeyForward,
475 | isKeyBackward
476 | }, userOptions);
477 | var state = {
478 | // containers given to createFocusTrap()
479 | // @type {Array<HTMLElement>}
480 | containers: [],
481 | // list of objects identifying tabbable nodes in `containers` in the trap
482 | // NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap
483 | // is active, but the trap should never get to a state where there isn't at least one group
484 | // with at least one tabbable node in it (that would lead to an error condition that would
485 | // result in an error being thrown)
486 | // @type {Array<{
487 | // container: HTMLElement,
488 | // tabbableNodes: Array<HTMLElement>, // empty if none
489 | // focusableNodes: Array<HTMLElement>, // empty if none
490 | // posTabIndexesFound: boolean,
491 | // firstTabbableNode: HTMLElement|undefined,
492 | // lastTabbableNode: HTMLElement|undefined,
493 | // firstDomTabbableNode: HTMLElement|undefined,
494 | // lastDomTabbableNode: HTMLElement|undefined,
495 | // nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
496 | // }>}
497 | containerGroups: [],
498 | // same order/length as `containers` list
499 | // references to objects in `containerGroups`, but only those that actually have
500 | // tabbable nodes in them
501 | // NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
502 | // the same length
503 | tabbableGroups: [],
504 | nodeFocusedBeforeActivation: null,
505 | mostRecentlyFocusedNode: null,
506 | active: false,
507 | paused: false,
508 | manuallyPaused: false,
509 | // timer ID for when delayInitialFocus is true and initial focus in this trap
510 | // has been delayed during activation
511 | delayInitialFocusTimer: void 0,
512 | // the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
513 | recentNavEvent: void 0
514 | };
515 | var trap;
516 | var getOption = function getOption2(configOverrideOptions, optionName, configOptionName) {
517 | return configOverrideOptions && configOverrideOptions[optionName] !== void 0 ? configOverrideOptions[optionName] : config[configOptionName || optionName];
518 | };
519 | var findContainerIndex = function findContainerIndex2(element, event) {
520 | var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === "function" ? event.composedPath() : void 0;
521 | return state.containerGroups.findIndex(function(_ref) {
522 | var container = _ref.container, tabbableNodes = _ref.tabbableNodes;
523 | return container.contains(element) || // fall back to explicit tabbable search which will take into consideration any
524 | // web components if the `tabbableOptions.getShadowRoot` option was used for
525 | // the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
526 | // look inside web components even if open)
527 | (composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function(node) {
528 | return node === element;
529 | });
530 | });
531 | };
532 | var getNodeForOption = function getNodeForOption2(optionName) {
533 | var _ref2 = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, _ref2$hasFallback = _ref2.hasFallback, hasFallback = _ref2$hasFallback === void 0 ? false : _ref2$hasFallback, _ref2$params = _ref2.params, params = _ref2$params === void 0 ? [] : _ref2$params;
534 | var optionValue = config[optionName];
535 | if (typeof optionValue === "function") {
536 | optionValue = optionValue.apply(void 0, _toConsumableArray(params));
537 | }
538 | if (optionValue === true) {
539 | optionValue = void 0;
540 | }
541 | if (!optionValue) {
542 | if (optionValue === void 0 || optionValue === false) {
543 | return optionValue;
544 | }
545 | throw new Error("`".concat(optionName, "` was specified but was not a node, or did not return a node"));
546 | }
547 | var node = optionValue;
548 | if (typeof optionValue === "string") {
549 | try {
550 | node = doc.querySelector(optionValue);
551 | } catch (err) {
552 | throw new Error("`".concat(optionName, '` appears to be an invalid selector; error="').concat(err.message, '"'));
553 | }
554 | if (!node) {
555 | if (!hasFallback) {
556 | throw new Error("`".concat(optionName, "` as selector refers to no known node"));
557 | }
558 | }
559 | }
560 | return node;
561 | };
562 | var getInitialFocusNode = function getInitialFocusNode2() {
563 | var node = getNodeForOption("initialFocus", {
564 | hasFallback: true
565 | });
566 | if (node === false) {
567 | return false;
568 | }
569 | if (node === void 0 || node && !isFocusable(node, config.tabbableOptions)) {
570 | if (findContainerIndex(doc.activeElement) >= 0) {
571 | node = doc.activeElement;
572 | } else {
573 | var firstTabbableGroup = state.tabbableGroups[0];
574 | var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;
575 | node = firstTabbableNode || getNodeForOption("fallbackFocus");
576 | }
577 | } else if (node === null) {
578 | node = getNodeForOption("fallbackFocus");
579 | }
580 | if (!node) {
581 | throw new Error("Your focus-trap needs to have at least one focusable element");
582 | }
583 | return node;
584 | };
585 | var updateTabbableNodes = function updateTabbableNodes2() {
586 | state.containerGroups = state.containers.map(function(container) {
587 | var tabbableNodes = tabbable(container, config.tabbableOptions);
588 | var focusableNodes = focusable(container, config.tabbableOptions);
589 | var firstTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[0] : void 0;
590 | var lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : void 0;
591 | var firstDomTabbableNode = focusableNodes.find(function(node) {
592 | return isTabbable(node);
593 | });
594 | var lastDomTabbableNode = focusableNodes.slice().reverse().find(function(node) {
595 | return isTabbable(node);
596 | });
597 | var posTabIndexesFound = !!tabbableNodes.find(function(node) {
598 | return getTabIndex(node) > 0;
599 | });
600 | return {
601 | container,
602 | tabbableNodes,
603 | focusableNodes,
604 | /** True if at least one node with positive `tabindex` was found in this container. */
605 | posTabIndexesFound,
606 | /** First tabbable node in container, __tabindex__ order; `undefined` if none. */
607 | firstTabbableNode,
608 | /** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
609 | lastTabbableNode,
610 | // NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
611 | // would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
612 | // because that API doesn't work with Shadow DOM as well as it should (@see
613 | // https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
614 | // to address an edge case related to positive tabindex support, this seems like a much easier,
615 | // "close enough most of the time" alternative for positive tabindexes which should generally
616 | // be avoided anyway...
617 | /** First tabbable node in container, __DOM__ order; `undefined` if none. */
618 | firstDomTabbableNode,
619 | /** Last tabbable node in container, __DOM__ order; `undefined` if none. */
620 | lastDomTabbableNode,
621 | /**
622 | * Finds the __tabbable__ node that follows the given node in the specified direction,
623 | * in this container, if any.
624 | * @param {HTMLElement} node
625 | * @param {boolean} [forward] True if going in forward tab order; false if going
626 | * in reverse.
627 | * @returns {HTMLElement|undefined} The next tabbable node, if any.
628 | */
629 | nextTabbableNode: function nextTabbableNode(node) {
630 | var forward = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;
631 | var nodeIdx = tabbableNodes.indexOf(node);
632 | if (nodeIdx < 0) {
633 | if (forward) {
634 | return focusableNodes.slice(focusableNodes.indexOf(node) + 1).find(function(el) {
635 | return isTabbable(el);
636 | });
637 | }
638 | return focusableNodes.slice(0, focusableNodes.indexOf(node)).reverse().find(function(el) {
639 | return isTabbable(el);
640 | });
641 | }
642 | return tabbableNodes[nodeIdx + (forward ? 1 : -1)];
643 | }
644 | };
645 | });
646 | state.tabbableGroups = state.containerGroups.filter(function(group) {
647 | return group.tabbableNodes.length > 0;
648 | });
649 | if (state.tabbableGroups.length <= 0 && !getNodeForOption("fallbackFocus")) {
650 | throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");
651 | }
652 | if (state.containerGroups.find(function(g) {
653 | return g.posTabIndexesFound;
654 | }) && state.containerGroups.length > 1) {
655 | throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.");
656 | }
657 | };
658 | var _getActiveElement = function getActiveElement(el) {
659 | var activeElement = el.activeElement;
660 | if (!activeElement) {
661 | return;
662 | }
663 | if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {
664 | return _getActiveElement(activeElement.shadowRoot);
665 | }
666 | return activeElement;
667 | };
668 | var _tryFocus = function tryFocus(node) {
669 | if (node === false) {
670 | return;
671 | }
672 | if (node === _getActiveElement(document)) {
673 | return;
674 | }
675 | if (!node || !node.focus) {
676 | _tryFocus(getInitialFocusNode());
677 | return;
678 | }
679 | node.focus({
680 | preventScroll: !!config.preventScroll
681 | });
682 | state.mostRecentlyFocusedNode = node;
683 | if (isSelectableInput(node)) {
684 | node.select();
685 | }
686 | };
687 | var getReturnFocusNode = function getReturnFocusNode2(previousActiveElement) {
688 | var node = getNodeForOption("setReturnFocus", {
689 | params: [previousActiveElement]
690 | });
691 | return node ? node : node === false ? false : previousActiveElement;
692 | };
693 | var findNextNavNode = function findNextNavNode2(_ref3) {
694 | var target = _ref3.target, event = _ref3.event, _ref3$isBackward = _ref3.isBackward, isBackward = _ref3$isBackward === void 0 ? false : _ref3$isBackward;
695 | target = target || getActualTarget(event);
696 | updateTabbableNodes();
697 | var destinationNode = null;
698 | if (state.tabbableGroups.length > 0) {
699 | var containerIndex = findContainerIndex(target, event);
700 | var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : void 0;
701 | if (containerIndex < 0) {
702 | if (isBackward) {
703 | destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;
704 | } else {
705 | destinationNode = state.tabbableGroups[0].firstTabbableNode;
706 | }
707 | } else if (isBackward) {
708 | var startOfGroupIndex = state.tabbableGroups.findIndex(function(_ref4) {
709 | var firstTabbableNode = _ref4.firstTabbableNode;
710 | return target === firstTabbableNode;
711 | });
712 | if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
713 | startOfGroupIndex = containerIndex;
714 | }
715 | if (startOfGroupIndex >= 0) {
716 | var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1;
717 | var destinationGroup = state.tabbableGroups[destinationGroupIndex];
718 | destinationNode = getTabIndex(target) >= 0 ? destinationGroup.lastTabbableNode : destinationGroup.lastDomTabbableNode;
719 | } else if (!isTabEvent(event)) {
720 | destinationNode = containerGroup.nextTabbableNode(target, false);
721 | }
722 | } else {
723 | var lastOfGroupIndex = state.tabbableGroups.findIndex(function(_ref5) {
724 | var lastTabbableNode = _ref5.lastTabbableNode;
725 | return target === lastTabbableNode;
726 | });
727 | if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
728 | lastOfGroupIndex = containerIndex;
729 | }
730 | if (lastOfGroupIndex >= 0) {
731 | var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
732 | var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
733 | destinationNode = getTabIndex(target) >= 0 ? _destinationGroup.firstTabbableNode : _destinationGroup.firstDomTabbableNode;
734 | } else if (!isTabEvent(event)) {
735 | destinationNode = containerGroup.nextTabbableNode(target);
736 | }
737 | }
738 | } else {
739 | destinationNode = getNodeForOption("fallbackFocus");
740 | }
741 | return destinationNode;
742 | };
743 | var checkPointerDown = function checkPointerDown2(e) {
744 | var target = getActualTarget(e);
745 | if (findContainerIndex(target, e) >= 0) {
746 | return;
747 | }
748 | if (valueOrHandler(config.clickOutsideDeactivates, e)) {
749 | trap.deactivate({
750 | // NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
751 | // which will result in the outside click setting focus to the node
752 | // that was clicked (and if not focusable, to "nothing"); by setting
753 | // `returnFocus: true`, we'll attempt to re-focus the node originally-focused
754 | // on activation (or the configured `setReturnFocus` node), whether the
755 | // outside click was on a focusable node or not
756 | returnFocus: config.returnFocusOnDeactivate
757 | });
758 | return;
759 | }
760 | if (valueOrHandler(config.allowOutsideClick, e)) {
761 | return;
762 | }
763 | e.preventDefault();
764 | };
765 | var checkFocusIn = function checkFocusIn2(event) {
766 | var target = getActualTarget(event);
767 | var targetContained = findContainerIndex(target, event) >= 0;
768 | if (targetContained || target instanceof Document) {
769 | if (targetContained) {
770 | state.mostRecentlyFocusedNode = target;
771 | }
772 | } else {
773 | event.stopImmediatePropagation();
774 | var nextNode;
775 | var navAcrossContainers = true;
776 | if (state.mostRecentlyFocusedNode) {
777 | if (getTabIndex(state.mostRecentlyFocusedNode) > 0) {
778 | var mruContainerIdx = findContainerIndex(state.mostRecentlyFocusedNode);
779 | var tabbableNodes = state.containerGroups[mruContainerIdx].tabbableNodes;
780 | if (tabbableNodes.length > 0) {
781 | var mruTabIdx = tabbableNodes.findIndex(function(node) {
782 | return node === state.mostRecentlyFocusedNode;
783 | });
784 | if (mruTabIdx >= 0) {
785 | if (config.isKeyForward(state.recentNavEvent)) {
786 | if (mruTabIdx + 1 < tabbableNodes.length) {
787 | nextNode = tabbableNodes[mruTabIdx + 1];
788 | navAcrossContainers = false;
789 | }
790 | } else {
791 | if (mruTabIdx - 1 >= 0) {
792 | nextNode = tabbableNodes[mruTabIdx - 1];
793 | navAcrossContainers = false;
794 | }
795 | }
796 | }
797 | }
798 | } else {
799 | if (!state.containerGroups.some(function(g) {
800 | return g.tabbableNodes.some(function(n) {
801 | return getTabIndex(n) > 0;
802 | });
803 | })) {
804 | navAcrossContainers = false;
805 | }
806 | }
807 | } else {
808 | navAcrossContainers = false;
809 | }
810 | if (navAcrossContainers) {
811 | nextNode = findNextNavNode({
812 | // move FROM the MRU node, not event-related node (which will be the node that is
813 | // outside the trap causing the focus escape we're trying to fix)
814 | target: state.mostRecentlyFocusedNode,
815 | isBackward: config.isKeyBackward(state.recentNavEvent)
816 | });
817 | }
818 | if (nextNode) {
819 | _tryFocus(nextNode);
820 | } else {
821 | _tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());
822 | }
823 | }
824 | state.recentNavEvent = void 0;
825 | };
826 | var checkKeyNav = function checkKeyNav2(event) {
827 | var isBackward = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
828 | state.recentNavEvent = event;
829 | var destinationNode = findNextNavNode({
830 | event,
831 | isBackward
832 | });
833 | if (destinationNode) {
834 | if (isTabEvent(event)) {
835 | event.preventDefault();
836 | }
837 | _tryFocus(destinationNode);
838 | }
839 | };
840 | var checkTabKey = function checkTabKey2(event) {
841 | if (config.isKeyForward(event) || config.isKeyBackward(event)) {
842 | checkKeyNav(event, config.isKeyBackward(event));
843 | }
844 | };
845 | var checkEscapeKey = function checkEscapeKey2(event) {
846 | if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {
847 | event.preventDefault();
848 | trap.deactivate();
849 | }
850 | };
851 | var checkClick = function checkClick2(e) {
852 | var target = getActualTarget(e);
853 | if (findContainerIndex(target, e) >= 0) {
854 | return;
855 | }
856 | if (valueOrHandler(config.clickOutsideDeactivates, e)) {
857 | return;
858 | }
859 | if (valueOrHandler(config.allowOutsideClick, e)) {
860 | return;
861 | }
862 | e.preventDefault();
863 | e.stopImmediatePropagation();
864 | };
865 | var addListeners = function addListeners2() {
866 | if (!state.active) {
867 | return;
868 | }
869 | activeFocusTraps.activateTrap(trapStack, trap);
870 | state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function() {
871 | _tryFocus(getInitialFocusNode());
872 | }) : _tryFocus(getInitialFocusNode());
873 | doc.addEventListener("focusin", checkFocusIn, true);
874 | doc.addEventListener("mousedown", checkPointerDown, {
875 | capture: true,
876 | passive: false
877 | });
878 | doc.addEventListener("touchstart", checkPointerDown, {
879 | capture: true,
880 | passive: false
881 | });
882 | doc.addEventListener("click", checkClick, {
883 | capture: true,
884 | passive: false
885 | });
886 | doc.addEventListener("keydown", checkTabKey, {
887 | capture: true,
888 | passive: false
889 | });
890 | doc.addEventListener("keydown", checkEscapeKey);
891 | return trap;
892 | };
893 | var removeListeners = function removeListeners2() {
894 | if (!state.active) {
895 | return;
896 | }
897 | doc.removeEventListener("focusin", checkFocusIn, true);
898 | doc.removeEventListener("mousedown", checkPointerDown, true);
899 | doc.removeEventListener("touchstart", checkPointerDown, true);
900 | doc.removeEventListener("click", checkClick, true);
901 | doc.removeEventListener("keydown", checkTabKey, true);
902 | doc.removeEventListener("keydown", checkEscapeKey);
903 | return trap;
904 | };
905 | var checkDomRemoval = function checkDomRemoval2(mutations) {
906 | var isFocusedNodeRemoved = mutations.some(function(mutation) {
907 | var removedNodes = Array.from(mutation.removedNodes);
908 | return removedNodes.some(function(node) {
909 | return node === state.mostRecentlyFocusedNode;
910 | });
911 | });
912 | if (isFocusedNodeRemoved) {
913 | _tryFocus(getInitialFocusNode());
914 | }
915 | };
916 | var mutationObserver = typeof window !== "undefined" && "MutationObserver" in window ? new MutationObserver(checkDomRemoval) : void 0;
917 | var updateObservedNodes = function updateObservedNodes2() {
918 | if (!mutationObserver) {
919 | return;
920 | }
921 | mutationObserver.disconnect();
922 | if (state.active && !state.paused) {
923 | state.containers.map(function(container) {
924 | mutationObserver.observe(container, {
925 | subtree: true,
926 | childList: true
927 | });
928 | });
929 | }
930 | };
931 | trap = {
932 | get active() {
933 | return state.active;
934 | },
935 | get paused() {
936 | return state.paused;
937 | },
938 | activate: function activate(activateOptions) {
939 | if (state.active) {
940 | return this;
941 | }
942 | var onActivate = getOption(activateOptions, "onActivate");
943 | var onPostActivate = getOption(activateOptions, "onPostActivate");
944 | var checkCanFocusTrap = getOption(activateOptions, "checkCanFocusTrap");
945 | if (!checkCanFocusTrap) {
946 | updateTabbableNodes();
947 | }
948 | state.active = true;
949 | state.paused = false;
950 | state.nodeFocusedBeforeActivation = _getActiveElement(doc);
951 | onActivate === null || onActivate === void 0 || onActivate();
952 | var finishActivation = function finishActivation2() {
953 | if (checkCanFocusTrap) {
954 | updateTabbableNodes();
955 | }
956 | addListeners();
957 | updateObservedNodes();
958 | onPostActivate === null || onPostActivate === void 0 || onPostActivate();
959 | };
960 | if (checkCanFocusTrap) {
961 | checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);
962 | return this;
963 | }
964 | finishActivation();
965 | return this;
966 | },
967 | deactivate: function deactivate(deactivateOptions) {
968 | if (!state.active) {
969 | return this;
970 | }
971 | var options = _objectSpread2({
972 | onDeactivate: config.onDeactivate,
973 | onPostDeactivate: config.onPostDeactivate,
974 | checkCanReturnFocus: config.checkCanReturnFocus
975 | }, deactivateOptions);
976 | clearTimeout(state.delayInitialFocusTimer);
977 | state.delayInitialFocusTimer = void 0;
978 | removeListeners();
979 | state.active = false;
980 | state.paused = false;
981 | updateObservedNodes();
982 | activeFocusTraps.deactivateTrap(trapStack, trap);
983 | var onDeactivate = getOption(options, "onDeactivate");
984 | var onPostDeactivate = getOption(options, "onPostDeactivate");
985 | var checkCanReturnFocus = getOption(options, "checkCanReturnFocus");
986 | var returnFocus = getOption(options, "returnFocus", "returnFocusOnDeactivate");
987 | onDeactivate === null || onDeactivate === void 0 || onDeactivate();
988 | var finishDeactivation = function finishDeactivation2() {
989 | delay(function() {
990 | if (returnFocus) {
991 | _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
992 | }
993 | onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate();
994 | });
995 | };
996 | if (returnFocus && checkCanReturnFocus) {
997 | checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);
998 | return this;
999 | }
1000 | finishDeactivation();
1001 | return this;
1002 | },
1003 | pause: function pause(pauseOptions) {
1004 | if (!state.active) {
1005 | return this;
1006 | }
1007 | state.manuallyPaused = true;
1008 | return this._setPausedState(true, pauseOptions);
1009 | },
1010 | unpause: function unpause(unpauseOptions) {
1011 | if (!state.active) {
1012 | return this;
1013 | }
1014 | state.manuallyPaused = false;
1015 | if (trapStack[trapStack.length - 1] !== this) {
1016 | return this;
1017 | }
1018 | return this._setPausedState(false, unpauseOptions);
1019 | },
1020 | updateContainerElements: function updateContainerElements(containerElements) {
1021 | var elementsAsArray = [].concat(containerElements).filter(Boolean);
1022 | state.containers = elementsAsArray.map(function(element) {
1023 | return typeof element === "string" ? doc.querySelector(element) : element;
1024 | });
1025 | if (state.active) {
1026 | updateTabbableNodes();
1027 | }
1028 | updateObservedNodes();
1029 | return this;
1030 | }
1031 | };
1032 | Object.defineProperties(trap, {
1033 | _isManuallyPaused: {
1034 | value: function value() {
1035 | return state.manuallyPaused;
1036 | }
1037 | },
1038 | _setPausedState: {
1039 | value: function value(paused, options) {
1040 | if (state.paused === paused) {
1041 | return this;
1042 | }
1043 | state.paused = paused;
1044 | if (paused) {
1045 | var onPause = getOption(options, "onPause");
1046 | var onPostPause = getOption(options, "onPostPause");
1047 | onPause === null || onPause === void 0 || onPause();
1048 | removeListeners();
1049 | updateObservedNodes();
1050 | onPostPause === null || onPostPause === void 0 || onPostPause();
1051 | } else {
1052 | var onUnpause = getOption(options, "onUnpause");
1053 | var onPostUnpause = getOption(options, "onPostUnpause");
1054 | onUnpause === null || onUnpause === void 0 || onUnpause();
1055 | updateTabbableNodes();
1056 | addListeners();
1057 | updateObservedNodes();
1058 | onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause();
1059 | }
1060 | return this;
1061 | }
1062 | }
1063 | });
1064 | trap.updateContainerElements(elements);
1065 | return trap;
1066 | };
1067 |
1068 | // node_modules/@vueuse/integrations/useFocusTrap.mjs
1069 | function useFocusTrap(target, options = {}) {
1070 | let trap;
1071 | const { immediate, ...focusTrapOptions } = options;
1072 | const hasFocus = shallowRef(false);
1073 | const isPaused = shallowRef(false);
1074 | const activate = (opts) => trap && trap.activate(opts);
1075 | const deactivate = (opts) => trap && trap.deactivate(opts);
1076 | const pause = () => {
1077 | if (trap) {
1078 | trap.pause();
1079 | isPaused.value = true;
1080 | }
1081 | };
1082 | const unpause = () => {
1083 | if (trap) {
1084 | trap.unpause();
1085 | isPaused.value = false;
1086 | }
1087 | };
1088 | const targets = computed(() => {
1089 | const _targets = toValue(target);
1090 | return toArray(_targets).map((el) => {
1091 | const _el = toValue(el);
1092 | return typeof _el === "string" ? _el : unrefElement(_el);
1093 | }).filter(notNullish);
1094 | });
1095 | watch(
1096 | targets,
1097 | (els) => {
1098 | if (!els.length)
1099 | return;
1100 | trap = createFocusTrap(els, {
1101 | ...focusTrapOptions,
1102 | onActivate() {
1103 | hasFocus.value = true;
1104 | if (options.onActivate)
1105 | options.onActivate();
1106 | },
1107 | onDeactivate() {
1108 | hasFocus.value = false;
1109 | if (options.onDeactivate)
1110 | options.onDeactivate();
1111 | }
1112 | });
1113 | if (immediate)
1114 | activate();
1115 | },
1116 | { flush: "post" }
1117 | );
1118 | tryOnScopeDispose(() => deactivate());
1119 | return {
1120 | hasFocus,
1121 | isPaused,
1122 | activate,
1123 | deactivate,
1124 | pause,
1125 | unpause
1126 | };
1127 | }
1128 | export {
1129 | useFocusTrap
1130 | };
1131 | /*! Bundled license information:
1132 |
1133 | tabbable/dist/index.esm.js:
1134 | (*!
1135 | * tabbable 6.2.0
1136 | * @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
1137 | *)
1138 |
1139 | focus-trap/dist/focus-trap.esm.js:
1140 | (*!
1141 | * focus-trap 7.6.5
1142 | * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
1143 | *)
1144 | */
1145 | //# sourceMappingURL=vitepress___@vueuse_integrations_useFocusTrap.js.map
1146 |
```
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js:
--------------------------------------------------------------------------------
```javascript
1 | // node_modules/mark.js/src/lib/domiterator.js
2 | var DOMIterator = class _DOMIterator {
3 | /**
4 | * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
5 | * element, an array of DOM elements, a NodeList or a selector
6 | * @param {boolean} [iframes=true] - A boolean indicating if iframes should
7 | * be handled
8 | * @param {string[]} [exclude=[]] - An array containing exclusion selectors
9 | * for iframes
10 | * @param {number} [iframesTimeout=5000] - A number indicating the ms to
11 | * wait before an iframe should be skipped, in case the load event isn't
12 | * fired. This also applies if the user is offline and the resource of the
13 | * iframe is online (either by the browsers "offline" mode or because
14 | * there's no internet connection)
15 | */
16 | constructor(ctx, iframes = true, exclude = [], iframesTimeout = 5e3) {
17 | this.ctx = ctx;
18 | this.iframes = iframes;
19 | this.exclude = exclude;
20 | this.iframesTimeout = iframesTimeout;
21 | }
22 | /**
23 | * Checks if the specified DOM element matches the selector
24 | * @param {HTMLElement} element - The DOM element
25 | * @param {string|string[]} selector - The selector or an array with
26 | * selectors
27 | * @return {boolean}
28 | * @access public
29 | */
30 | static matches(element, selector) {
31 | const selectors = typeof selector === "string" ? [selector] : selector, fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector;
32 | if (fn) {
33 | let match = false;
34 | selectors.every((sel) => {
35 | if (fn.call(element, sel)) {
36 | match = true;
37 | return false;
38 | }
39 | return true;
40 | });
41 | return match;
42 | } else {
43 | return false;
44 | }
45 | }
46 | /**
47 | * Returns all contexts filtered by duplicates (even nested)
48 | * @return {HTMLElement[]} - An array containing DOM contexts
49 | * @access protected
50 | */
51 | getContexts() {
52 | let ctx, filteredCtx = [];
53 | if (typeof this.ctx === "undefined" || !this.ctx) {
54 | ctx = [];
55 | } else if (NodeList.prototype.isPrototypeOf(this.ctx)) {
56 | ctx = Array.prototype.slice.call(this.ctx);
57 | } else if (Array.isArray(this.ctx)) {
58 | ctx = this.ctx;
59 | } else if (typeof this.ctx === "string") {
60 | ctx = Array.prototype.slice.call(
61 | document.querySelectorAll(this.ctx)
62 | );
63 | } else {
64 | ctx = [this.ctx];
65 | }
66 | ctx.forEach((ctx2) => {
67 | const isDescendant = filteredCtx.filter((contexts) => {
68 | return contexts.contains(ctx2);
69 | }).length > 0;
70 | if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) {
71 | filteredCtx.push(ctx2);
72 | }
73 | });
74 | return filteredCtx;
75 | }
76 | /**
77 | * @callback DOMIterator~getIframeContentsSuccessCallback
78 | * @param {HTMLDocument} contents - The contentDocument of the iframe
79 | */
80 | /**
81 | * Calls the success callback function with the iframe document. If it can't
82 | * be accessed it calls the error callback function
83 | * @param {HTMLElement} ifr - The iframe DOM element
84 | * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
85 | * @param {function} [errorFn]
86 | * @access protected
87 | */
88 | getIframeContents(ifr, successFn, errorFn = () => {
89 | }) {
90 | let doc;
91 | try {
92 | const ifrWin = ifr.contentWindow;
93 | doc = ifrWin.document;
94 | if (!ifrWin || !doc) {
95 | throw new Error("iframe inaccessible");
96 | }
97 | } catch (e) {
98 | errorFn();
99 | }
100 | if (doc) {
101 | successFn(doc);
102 | }
103 | }
104 | /**
105 | * Checks if an iframe is empty (if about:blank is the shown page)
106 | * @param {HTMLElement} ifr - The iframe DOM element
107 | * @return {boolean}
108 | * @access protected
109 | */
110 | isIframeBlank(ifr) {
111 | const bl = "about:blank", src = ifr.getAttribute("src").trim(), href = ifr.contentWindow.location.href;
112 | return href === bl && src !== bl && src;
113 | }
114 | /**
115 | * Observes the onload event of an iframe and calls the success callback or
116 | * the error callback if the iframe is inaccessible. If the event isn't
117 | * fired within the specified {@link DOMIterator#iframesTimeout}, then it'll
118 | * call the error callback too
119 | * @param {HTMLElement} ifr - The iframe DOM element
120 | * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
121 | * @param {function} errorFn
122 | * @access protected
123 | */
124 | observeIframeLoad(ifr, successFn, errorFn) {
125 | let called = false, tout = null;
126 | const listener = () => {
127 | if (called) {
128 | return;
129 | }
130 | called = true;
131 | clearTimeout(tout);
132 | try {
133 | if (!this.isIframeBlank(ifr)) {
134 | ifr.removeEventListener("load", listener);
135 | this.getIframeContents(ifr, successFn, errorFn);
136 | }
137 | } catch (e) {
138 | errorFn();
139 | }
140 | };
141 | ifr.addEventListener("load", listener);
142 | tout = setTimeout(listener, this.iframesTimeout);
143 | }
144 | /**
145 | * Callback when the iframe is ready
146 | * @callback DOMIterator~onIframeReadySuccessCallback
147 | * @param {HTMLDocument} contents - The contentDocument of the iframe
148 | */
149 | /**
150 | * Callback if the iframe can't be accessed
151 | * @callback DOMIterator~onIframeReadyErrorCallback
152 | */
153 | /**
154 | * Calls the callback if the specified iframe is ready for DOM access
155 | * @param {HTMLElement} ifr - The iframe DOM element
156 | * @param {DOMIterator~onIframeReadySuccessCallback} successFn - Success
157 | * callback
158 | * @param {DOMIterator~onIframeReadyErrorCallback} errorFn - Error callback
159 | * @see {@link http://stackoverflow.com/a/36155560/3894981} for
160 | * background information
161 | * @access protected
162 | */
163 | onIframeReady(ifr, successFn, errorFn) {
164 | try {
165 | if (ifr.contentWindow.document.readyState === "complete") {
166 | if (this.isIframeBlank(ifr)) {
167 | this.observeIframeLoad(ifr, successFn, errorFn);
168 | } else {
169 | this.getIframeContents(ifr, successFn, errorFn);
170 | }
171 | } else {
172 | this.observeIframeLoad(ifr, successFn, errorFn);
173 | }
174 | } catch (e) {
175 | errorFn();
176 | }
177 | }
178 | /**
179 | * Callback when all iframes are ready for DOM access
180 | * @callback DOMIterator~waitForIframesDoneCallback
181 | */
182 | /**
183 | * Iterates over all iframes and calls the done callback when all of them
184 | * are ready for DOM access (including nested ones)
185 | * @param {HTMLElement} ctx - The context DOM element
186 | * @param {DOMIterator~waitForIframesDoneCallback} done - Done callback
187 | */
188 | waitForIframes(ctx, done) {
189 | let eachCalled = 0;
190 | this.forEachIframe(ctx, () => true, (ifr) => {
191 | eachCalled++;
192 | this.waitForIframes(ifr.querySelector("html"), () => {
193 | if (!--eachCalled) {
194 | done();
195 | }
196 | });
197 | }, (handled) => {
198 | if (!handled) {
199 | done();
200 | }
201 | });
202 | }
203 | /**
204 | * Callback allowing to filter an iframe. Must return true when the element
205 | * should remain, otherwise false
206 | * @callback DOMIterator~forEachIframeFilterCallback
207 | * @param {HTMLElement} iframe - The iframe DOM element
208 | */
209 | /**
210 | * Callback for each iframe content
211 | * @callback DOMIterator~forEachIframeEachCallback
212 | * @param {HTMLElement} content - The iframe document
213 | */
214 | /**
215 | * Callback if all iframes inside the context were handled
216 | * @callback DOMIterator~forEachIframeEndCallback
217 | * @param {number} handled - The number of handled iframes (those who
218 | * wheren't filtered)
219 | */
220 | /**
221 | * Iterates over all iframes inside the specified context and calls the
222 | * callbacks when they're ready. Filters iframes based on the instance
223 | * exclusion selectors
224 | * @param {HTMLElement} ctx - The context DOM element
225 | * @param {DOMIterator~forEachIframeFilterCallback} filter - Filter callback
226 | * @param {DOMIterator~forEachIframeEachCallback} each - Each callback
227 | * @param {DOMIterator~forEachIframeEndCallback} [end] - End callback
228 | * @access protected
229 | */
230 | forEachIframe(ctx, filter, each, end = () => {
231 | }) {
232 | let ifr = ctx.querySelectorAll("iframe"), open = ifr.length, handled = 0;
233 | ifr = Array.prototype.slice.call(ifr);
234 | const checkEnd = () => {
235 | if (--open <= 0) {
236 | end(handled);
237 | }
238 | };
239 | if (!open) {
240 | checkEnd();
241 | }
242 | ifr.forEach((ifr2) => {
243 | if (_DOMIterator.matches(ifr2, this.exclude)) {
244 | checkEnd();
245 | } else {
246 | this.onIframeReady(ifr2, (con) => {
247 | if (filter(ifr2)) {
248 | handled++;
249 | each(con);
250 | }
251 | checkEnd();
252 | }, checkEnd);
253 | }
254 | });
255 | }
256 | /**
257 | * Creates a NodeIterator on the specified context
258 | * @see {@link https://developer.mozilla.org/en/docs/Web/API/NodeIterator}
259 | * @param {HTMLElement} ctx - The context DOM element
260 | * @param {DOMIterator~whatToShow} whatToShow
261 | * @param {DOMIterator~filterCb} filter
262 | * @return {NodeIterator}
263 | * @access protected
264 | */
265 | createIterator(ctx, whatToShow, filter) {
266 | return document.createNodeIterator(ctx, whatToShow, filter, false);
267 | }
268 | /**
269 | * Creates an instance of DOMIterator in an iframe
270 | * @param {HTMLDocument} contents - Iframe document
271 | * @return {DOMIterator}
272 | * @access protected
273 | */
274 | createInstanceOnIframe(contents) {
275 | return new _DOMIterator(contents.querySelector("html"), this.iframes);
276 | }
277 | /**
278 | * Checks if an iframe occurs between two nodes, more specifically if an
279 | * iframe occurs before the specified node and after the specified prevNode
280 | * @param {HTMLElement} node - The node that should occur after the iframe
281 | * @param {HTMLElement} prevNode - The node that should occur before the
282 | * iframe
283 | * @param {HTMLElement} ifr - The iframe to check against
284 | * @return {boolean}
285 | * @access protected
286 | */
287 | compareNodeIframe(node, prevNode, ifr) {
288 | const compCurr = node.compareDocumentPosition(ifr), prev = Node.DOCUMENT_POSITION_PRECEDING;
289 | if (compCurr & prev) {
290 | if (prevNode !== null) {
291 | const compPrev = prevNode.compareDocumentPosition(ifr), after = Node.DOCUMENT_POSITION_FOLLOWING;
292 | if (compPrev & after) {
293 | return true;
294 | }
295 | } else {
296 | return true;
297 | }
298 | }
299 | return false;
300 | }
301 | /**
302 | * @typedef {DOMIterator~getIteratorNodeReturn}
303 | * @type {object.<string>}
304 | * @property {HTMLElement} prevNode - The previous node or null if there is
305 | * no
306 | * @property {HTMLElement} node - The current node
307 | */
308 | /**
309 | * Returns the previous and current node of the specified iterator
310 | * @param {NodeIterator} itr - The iterator
311 | * @return {DOMIterator~getIteratorNodeReturn}
312 | * @access protected
313 | */
314 | getIteratorNode(itr) {
315 | const prevNode = itr.previousNode();
316 | let node;
317 | if (prevNode === null) {
318 | node = itr.nextNode();
319 | } else {
320 | node = itr.nextNode() && itr.nextNode();
321 | }
322 | return {
323 | prevNode,
324 | node
325 | };
326 | }
327 | /**
328 | * An array containing objects. The object key "val" contains an iframe
329 | * DOM element. The object key "handled" contains a boolean indicating if
330 | * the iframe was handled already.
331 | * It wouldn't be enough to save all open or all already handled iframes.
332 | * The information of open iframes is necessary because they may occur after
333 | * all other text nodes (and compareNodeIframe would never be true). The
334 | * information of already handled iframes is necessary as otherwise they may
335 | * be handled multiple times
336 | * @typedef DOMIterator~checkIframeFilterIfr
337 | * @type {object[]}
338 | */
339 | /**
340 | * Checks if an iframe wasn't handled already and if so, calls
341 | * {@link DOMIterator#compareNodeIframe} to check if it should be handled.
342 | * Information wheter an iframe was or wasn't handled is given within the
343 | * <code>ifr</code> dictionary
344 | * @param {HTMLElement} node - The node that should occur after the iframe
345 | * @param {HTMLElement} prevNode - The node that should occur before the
346 | * iframe
347 | * @param {HTMLElement} currIfr - The iframe to check
348 | * @param {DOMIterator~checkIframeFilterIfr} ifr - The iframe dictionary.
349 | * Will be manipulated (by reference)
350 | * @return {boolean} Returns true when it should be handled, otherwise false
351 | * @access protected
352 | */
353 | checkIframeFilter(node, prevNode, currIfr, ifr) {
354 | let key = false, handled = false;
355 | ifr.forEach((ifrDict, i) => {
356 | if (ifrDict.val === currIfr) {
357 | key = i;
358 | handled = ifrDict.handled;
359 | }
360 | });
361 | if (this.compareNodeIframe(node, prevNode, currIfr)) {
362 | if (key === false && !handled) {
363 | ifr.push({
364 | val: currIfr,
365 | handled: true
366 | });
367 | } else if (key !== false && !handled) {
368 | ifr[key].handled = true;
369 | }
370 | return true;
371 | }
372 | if (key === false) {
373 | ifr.push({
374 | val: currIfr,
375 | handled: false
376 | });
377 | }
378 | return false;
379 | }
380 | /**
381 | * Creates an iterator on all open iframes in the specified array and calls
382 | * the end callback when finished
383 | * @param {DOMIterator~checkIframeFilterIfr} ifr
384 | * @param {DOMIterator~whatToShow} whatToShow
385 | * @param {DOMIterator~forEachNodeCallback} eCb - Each callback
386 | * @param {DOMIterator~filterCb} fCb
387 | * @access protected
388 | */
389 | handleOpenIframes(ifr, whatToShow, eCb, fCb) {
390 | ifr.forEach((ifrDict) => {
391 | if (!ifrDict.handled) {
392 | this.getIframeContents(ifrDict.val, (con) => {
393 | this.createInstanceOnIframe(con).forEachNode(
394 | whatToShow,
395 | eCb,
396 | fCb
397 | );
398 | });
399 | }
400 | });
401 | }
402 | /**
403 | * Iterates through all nodes in the specified context and handles iframe
404 | * nodes at the correct position
405 | * @param {DOMIterator~whatToShow} whatToShow
406 | * @param {HTMLElement} ctx - The context
407 | * @param {DOMIterator~forEachNodeCallback} eachCb - Each callback
408 | * @param {DOMIterator~filterCb} filterCb - Filter callback
409 | * @param {DOMIterator~forEachNodeEndCallback} doneCb - End callback
410 | * @access protected
411 | */
412 | iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) {
413 | const itr = this.createIterator(ctx, whatToShow, filterCb);
414 | let ifr = [], elements = [], node, prevNode, retrieveNodes = () => {
415 | ({
416 | prevNode,
417 | node
418 | } = this.getIteratorNode(itr));
419 | return node;
420 | };
421 | while (retrieveNodes()) {
422 | if (this.iframes) {
423 | this.forEachIframe(ctx, (currIfr) => {
424 | return this.checkIframeFilter(node, prevNode, currIfr, ifr);
425 | }, (con) => {
426 | this.createInstanceOnIframe(con).forEachNode(
427 | whatToShow,
428 | (ifrNode) => elements.push(ifrNode),
429 | filterCb
430 | );
431 | });
432 | }
433 | elements.push(node);
434 | }
435 | elements.forEach((node2) => {
436 | eachCb(node2);
437 | });
438 | if (this.iframes) {
439 | this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb);
440 | }
441 | doneCb();
442 | }
443 | /**
444 | * Callback for each node
445 | * @callback DOMIterator~forEachNodeCallback
446 | * @param {HTMLElement} node - The DOM text node element
447 | */
448 | /**
449 | * Callback if all contexts were handled
450 | * @callback DOMIterator~forEachNodeEndCallback
451 | */
452 | /**
453 | * Iterates over all contexts and initializes
454 | * {@link DOMIterator#iterateThroughNodes iterateThroughNodes} on them
455 | * @param {DOMIterator~whatToShow} whatToShow
456 | * @param {DOMIterator~forEachNodeCallback} each - Each callback
457 | * @param {DOMIterator~filterCb} filter - Filter callback
458 | * @param {DOMIterator~forEachNodeEndCallback} done - End callback
459 | * @access public
460 | */
461 | forEachNode(whatToShow, each, filter, done = () => {
462 | }) {
463 | const contexts = this.getContexts();
464 | let open = contexts.length;
465 | if (!open) {
466 | done();
467 | }
468 | contexts.forEach((ctx) => {
469 | const ready = () => {
470 | this.iterateThroughNodes(whatToShow, ctx, each, filter, () => {
471 | if (--open <= 0) {
472 | done();
473 | }
474 | });
475 | };
476 | if (this.iframes) {
477 | this.waitForIframes(ctx, ready);
478 | } else {
479 | ready();
480 | }
481 | });
482 | }
483 | /**
484 | * Callback to filter nodes. Can return e.g. NodeFilter.FILTER_ACCEPT or
485 | * NodeFilter.FILTER_REJECT
486 | * @see {@link http://tinyurl.com/zdczmm2}
487 | * @callback DOMIterator~filterCb
488 | * @param {HTMLElement} node - The node to filter
489 | */
490 | /**
491 | * @typedef DOMIterator~whatToShow
492 | * @see {@link http://tinyurl.com/zfqqkx2}
493 | * @type {number}
494 | */
495 | };
496 |
497 | // node_modules/mark.js/src/lib/mark.js
498 | var Mark = class {
499 | // eslint-disable-line no-unused-vars
500 | /**
501 | * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
502 | * element, an array of DOM elements, a NodeList or a selector
503 | */
504 | constructor(ctx) {
505 | this.ctx = ctx;
506 | this.ie = false;
507 | const ua = window.navigator.userAgent;
508 | if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) {
509 | this.ie = true;
510 | }
511 | }
512 | /**
513 | * Options defined by the user. They will be initialized from one of the
514 | * public methods. See {@link Mark#mark}, {@link Mark#markRegExp},
515 | * {@link Mark#markRanges} and {@link Mark#unmark} for option properties.
516 | * @type {object}
517 | * @param {object} [val] - An object that will be merged with defaults
518 | * @access protected
519 | */
520 | set opt(val) {
521 | this._opt = Object.assign({}, {
522 | "element": "",
523 | "className": "",
524 | "exclude": [],
525 | "iframes": false,
526 | "iframesTimeout": 5e3,
527 | "separateWordSearch": true,
528 | "diacritics": true,
529 | "synonyms": {},
530 | "accuracy": "partially",
531 | "acrossElements": false,
532 | "caseSensitive": false,
533 | "ignoreJoiners": false,
534 | "ignoreGroups": 0,
535 | "ignorePunctuation": [],
536 | "wildcards": "disabled",
537 | "each": () => {
538 | },
539 | "noMatch": () => {
540 | },
541 | "filter": () => true,
542 | "done": () => {
543 | },
544 | "debug": false,
545 | "log": window.console
546 | }, val);
547 | }
548 | get opt() {
549 | return this._opt;
550 | }
551 | /**
552 | * An instance of DOMIterator
553 | * @type {DOMIterator}
554 | * @access protected
555 | */
556 | get iterator() {
557 | return new DOMIterator(
558 | this.ctx,
559 | this.opt.iframes,
560 | this.opt.exclude,
561 | this.opt.iframesTimeout
562 | );
563 | }
564 | /**
565 | * Logs a message if log is enabled
566 | * @param {string} msg - The message to log
567 | * @param {string} [level="debug"] - The log level, e.g. <code>warn</code>
568 | * <code>error</code>, <code>debug</code>
569 | * @access protected
570 | */
571 | log(msg, level = "debug") {
572 | const log = this.opt.log;
573 | if (!this.opt.debug) {
574 | return;
575 | }
576 | if (typeof log === "object" && typeof log[level] === "function") {
577 | log[level](`mark.js: ${msg}`);
578 | }
579 | }
580 | /**
581 | * Escapes a string for usage within a regular expression
582 | * @param {string} str - The string to escape
583 | * @return {string}
584 | * @access protected
585 | */
586 | escapeStr(str) {
587 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
588 | }
589 | /**
590 | * Creates a regular expression string to match the specified search
591 | * term including synonyms, diacritics and accuracy if defined
592 | * @param {string} str - The search term to be used
593 | * @return {string}
594 | * @access protected
595 | */
596 | createRegExp(str) {
597 | if (this.opt.wildcards !== "disabled") {
598 | str = this.setupWildcardsRegExp(str);
599 | }
600 | str = this.escapeStr(str);
601 | if (Object.keys(this.opt.synonyms).length) {
602 | str = this.createSynonymsRegExp(str);
603 | }
604 | if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
605 | str = this.setupIgnoreJoinersRegExp(str);
606 | }
607 | if (this.opt.diacritics) {
608 | str = this.createDiacriticsRegExp(str);
609 | }
610 | str = this.createMergedBlanksRegExp(str);
611 | if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
612 | str = this.createJoinersRegExp(str);
613 | }
614 | if (this.opt.wildcards !== "disabled") {
615 | str = this.createWildcardsRegExp(str);
616 | }
617 | str = this.createAccuracyRegExp(str);
618 | return str;
619 | }
620 | /**
621 | * Creates a regular expression string to match the defined synonyms
622 | * @param {string} str - The search term to be used
623 | * @return {string}
624 | * @access protected
625 | */
626 | createSynonymsRegExp(str) {
627 | const syn = this.opt.synonyms, sens = this.opt.caseSensitive ? "" : "i", joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : "";
628 | for (let index in syn) {
629 | if (syn.hasOwnProperty(index)) {
630 | const value = syn[index], k1 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(index) : this.escapeStr(index), k2 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(value) : this.escapeStr(value);
631 | if (k1 !== "" && k2 !== "") {
632 | str = str.replace(
633 | new RegExp(
634 | `(${this.escapeStr(k1)}|${this.escapeStr(k2)})`,
635 | `gm${sens}`
636 | ),
637 | joinerPlaceholder + `(${this.processSynomyms(k1)}|${this.processSynomyms(k2)})` + joinerPlaceholder
638 | );
639 | }
640 | }
641 | }
642 | return str;
643 | }
644 | /**
645 | * Setup synonyms to work with ignoreJoiners and or ignorePunctuation
646 | * @param {string} str - synonym key or value to process
647 | * @return {string} - processed synonym string
648 | */
649 | processSynomyms(str) {
650 | if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
651 | str = this.setupIgnoreJoinersRegExp(str);
652 | }
653 | return str;
654 | }
655 | /**
656 | * Sets up the regular expression string to allow later insertion of
657 | * wildcard regular expression matches
658 | * @param {string} str - The search term to be used
659 | * @return {string}
660 | * @access protected
661 | */
662 | setupWildcardsRegExp(str) {
663 | str = str.replace(/(?:\\)*\?/g, (val) => {
664 | return val.charAt(0) === "\\" ? "?" : "";
665 | });
666 | return str.replace(/(?:\\)*\*/g, (val) => {
667 | return val.charAt(0) === "\\" ? "*" : "";
668 | });
669 | }
670 | /**
671 | * Sets up the regular expression string to allow later insertion of
672 | * wildcard regular expression matches
673 | * @param {string} str - The search term to be used
674 | * @return {string}
675 | * @access protected
676 | */
677 | createWildcardsRegExp(str) {
678 | let spaces = this.opt.wildcards === "withSpaces";
679 | return str.replace(/\u0001/g, spaces ? "[\\S\\s]?" : "\\S?").replace(/\u0002/g, spaces ? "[\\S\\s]*?" : "\\S*");
680 | }
681 | /**
682 | * Sets up the regular expression string to allow later insertion of
683 | * designated characters (soft hyphens & zero width characters)
684 | * @param {string} str - The search term to be used
685 | * @return {string}
686 | * @access protected
687 | */
688 | setupIgnoreJoinersRegExp(str) {
689 | return str.replace(/[^(|)\\]/g, (val, indx, original) => {
690 | let nextChar = original.charAt(indx + 1);
691 | if (/[(|)\\]/.test(nextChar) || nextChar === "") {
692 | return val;
693 | } else {
694 | return val + "\0";
695 | }
696 | });
697 | }
698 | /**
699 | * Creates a regular expression string to allow ignoring of designated
700 | * characters (soft hyphens, zero width characters & punctuation) based on
701 | * the specified option values of <code>ignorePunctuation</code> and
702 | * <code>ignoreJoiners</code>
703 | * @param {string} str - The search term to be used
704 | * @return {string}
705 | * @access protected
706 | */
707 | createJoinersRegExp(str) {
708 | let joiner = [];
709 | const ignorePunctuation = this.opt.ignorePunctuation;
710 | if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) {
711 | joiner.push(this.escapeStr(ignorePunctuation.join("")));
712 | }
713 | if (this.opt.ignoreJoiners) {
714 | joiner.push("\\u00ad\\u200b\\u200c\\u200d");
715 | }
716 | return joiner.length ? str.split(/\u0000+/).join(`[${joiner.join("")}]*`) : str;
717 | }
718 | /**
719 | * Creates a regular expression string to match diacritics
720 | * @param {string} str - The search term to be used
721 | * @return {string}
722 | * @access protected
723 | */
724 | createDiacriticsRegExp(str) {
725 | const sens = this.opt.caseSensitive ? "" : "i", dct = this.opt.caseSensitive ? [
726 | "aàáảãạăằắẳẵặâầấẩẫậäåāą",
727 | "AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
728 | "cçćč",
729 | "CÇĆČ",
730 | "dđď",
731 | "DĐĎ",
732 | "eèéẻẽẹêềếểễệëěēę",
733 | "EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
734 | "iìíỉĩịîïī",
735 | "IÌÍỈĨỊÎÏĪ",
736 | "lł",
737 | "LŁ",
738 | "nñňń",
739 | "NÑŇŃ",
740 | "oòóỏõọôồốổỗộơởỡớờợöøō",
741 | "OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
742 | "rř",
743 | "RŘ",
744 | "sšśșş",
745 | "SŠŚȘŞ",
746 | "tťțţ",
747 | "TŤȚŢ",
748 | "uùúủũụưừứửữựûüůū",
749 | "UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
750 | "yýỳỷỹỵÿ",
751 | "YÝỲỶỸỴŸ",
752 | "zžżź",
753 | "ZŽŻŹ"
754 | ] : [
755 | "aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
756 | "cçćčCÇĆČ",
757 | "dđďDĐĎ",
758 | "eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
759 | "iìíỉĩịîïīIÌÍỈĨỊÎÏĪ",
760 | "lłLŁ",
761 | "nñňńNÑŇŃ",
762 | "oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
763 | "rřRŘ",
764 | "sšśșşSŠŚȘŞ",
765 | "tťțţTŤȚŢ",
766 | "uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
767 | "yýỳỷỹỵÿYÝỲỶỸỴŸ",
768 | "zžżźZŽŻŹ"
769 | ];
770 | let handled = [];
771 | str.split("").forEach((ch) => {
772 | dct.every((dct2) => {
773 | if (dct2.indexOf(ch) !== -1) {
774 | if (handled.indexOf(dct2) > -1) {
775 | return false;
776 | }
777 | str = str.replace(
778 | new RegExp(`[${dct2}]`, `gm${sens}`),
779 | `[${dct2}]`
780 | );
781 | handled.push(dct2);
782 | }
783 | return true;
784 | });
785 | });
786 | return str;
787 | }
788 | /**
789 | * Creates a regular expression string that merges whitespace characters
790 | * including subsequent ones into a single pattern, one or multiple
791 | * whitespaces
792 | * @param {string} str - The search term to be used
793 | * @return {string}
794 | * @access protected
795 | */
796 | createMergedBlanksRegExp(str) {
797 | return str.replace(/[\s]+/gmi, "[\\s]+");
798 | }
799 | /**
800 | * Creates a regular expression string to match the specified string with
801 | * the defined accuracy. As in the regular expression of "exactly" can be
802 | * a group containing a blank at the beginning, all regular expressions will
803 | * be created with two groups. The first group can be ignored (may contain
804 | * the said blank), the second contains the actual match
805 | * @param {string} str - The searm term to be used
806 | * @return {str}
807 | * @access protected
808 | */
809 | createAccuracyRegExp(str) {
810 | const chars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿";
811 | let acc = this.opt.accuracy, val = typeof acc === "string" ? acc : acc.value, ls = typeof acc === "string" ? [] : acc.limiters, lsJoin = "";
812 | ls.forEach((limiter) => {
813 | lsJoin += `|${this.escapeStr(limiter)}`;
814 | });
815 | switch (val) {
816 | case "partially":
817 | default:
818 | return `()(${str})`;
819 | case "complementary":
820 | lsJoin = "\\s" + (lsJoin ? lsJoin : this.escapeStr(chars));
821 | return `()([^${lsJoin}]*${str}[^${lsJoin}]*)`;
822 | case "exactly":
823 | return `(^|\\s${lsJoin})(${str})(?=$|\\s${lsJoin})`;
824 | }
825 | }
826 | /**
827 | * @typedef Mark~separatedKeywords
828 | * @type {object.<string>}
829 | * @property {array.<string>} keywords - The list of keywords
830 | * @property {number} length - The length
831 | */
832 | /**
833 | * Returns a list of keywords dependent on whether separate word search
834 | * was defined. Also it filters empty keywords
835 | * @param {array} sv - The array of keywords
836 | * @return {Mark~separatedKeywords}
837 | * @access protected
838 | */
839 | getSeparatedKeywords(sv) {
840 | let stack = [];
841 | sv.forEach((kw) => {
842 | if (!this.opt.separateWordSearch) {
843 | if (kw.trim() && stack.indexOf(kw) === -1) {
844 | stack.push(kw);
845 | }
846 | } else {
847 | kw.split(" ").forEach((kwSplitted) => {
848 | if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) {
849 | stack.push(kwSplitted);
850 | }
851 | });
852 | }
853 | });
854 | return {
855 | // sort because of https://git.io/v6USg
856 | "keywords": stack.sort((a, b) => {
857 | return b.length - a.length;
858 | }),
859 | "length": stack.length
860 | };
861 | }
862 | /**
863 | * Check if a value is a number
864 | * @param {number|string} value - the value to check;
865 | * numeric strings allowed
866 | * @return {boolean}
867 | * @access protected
868 | */
869 | isNumeric(value) {
870 | return Number(parseFloat(value)) == value;
871 | }
872 | /**
873 | * @typedef Mark~rangeObject
874 | * @type {object}
875 | * @property {number} start - The start position within the composite value
876 | * @property {number} length - The length of the string to mark within the
877 | * composite value.
878 | */
879 | /**
880 | * @typedef Mark~setOfRanges
881 | * @type {object[]}
882 | * @property {Mark~rangeObject}
883 | */
884 | /**
885 | * Returns a processed list of integer offset indexes that do not overlap
886 | * each other, and remove any string values or additional elements
887 | * @param {Mark~setOfRanges} array - unprocessed raw array
888 | * @return {Mark~setOfRanges} - processed array with any invalid entries
889 | * removed
890 | * @throws Will throw an error if an array of objects is not passed
891 | * @access protected
892 | */
893 | checkRanges(array) {
894 | if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== "[object Object]") {
895 | this.log("markRanges() will only accept an array of objects");
896 | this.opt.noMatch(array);
897 | return [];
898 | }
899 | const stack = [];
900 | let last = 0;
901 | array.sort((a, b) => {
902 | return a.start - b.start;
903 | }).forEach((item) => {
904 | let { start, end, valid } = this.callNoMatchOnInvalidRanges(item, last);
905 | if (valid) {
906 | item.start = start;
907 | item.length = end - start;
908 | stack.push(item);
909 | last = end;
910 | }
911 | });
912 | return stack;
913 | }
914 | /**
915 | * @typedef Mark~validObject
916 | * @type {object}
917 | * @property {number} start - The start position within the composite value
918 | * @property {number} end - The calculated end position within the composite
919 | * value.
920 | * @property {boolean} valid - boolean value indicating that the start and
921 | * calculated end range is valid
922 | */
923 | /**
924 | * Initial validation of ranges for markRanges. Preliminary checks are done
925 | * to ensure the start and length values exist and are not zero or non-
926 | * numeric
927 | * @param {Mark~rangeObject} range - the current range object
928 | * @param {number} last - last index of range
929 | * @return {Mark~validObject}
930 | * @access protected
931 | */
932 | callNoMatchOnInvalidRanges(range, last) {
933 | let start, end, valid = false;
934 | if (range && typeof range.start !== "undefined") {
935 | start = parseInt(range.start, 10);
936 | end = start + parseInt(range.length, 10);
937 | if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) {
938 | valid = true;
939 | } else {
940 | this.log(
941 | `Ignoring invalid or overlapping range: ${JSON.stringify(range)}`
942 | );
943 | this.opt.noMatch(range);
944 | }
945 | } else {
946 | this.log(`Ignoring invalid range: ${JSON.stringify(range)}`);
947 | this.opt.noMatch(range);
948 | }
949 | return {
950 | start,
951 | end,
952 | valid
953 | };
954 | }
955 | /**
956 | * Check valid range for markRanges. Check ranges with access to the context
957 | * string. Range values are double checked, lengths that extend the mark
958 | * beyond the string length are limitied and ranges containing only
959 | * whitespace are ignored
960 | * @param {Mark~rangeObject} range - the current range object
961 | * @param {number} originalLength - original length of the context string
962 | * @param {string} string - current content string
963 | * @return {Mark~validObject}
964 | * @access protected
965 | */
966 | checkWhitespaceRanges(range, originalLength, string) {
967 | let end, valid = true, max = string.length, offset = originalLength - max, start = parseInt(range.start, 10) - offset;
968 | start = start > max ? max : start;
969 | end = start + parseInt(range.length, 10);
970 | if (end > max) {
971 | end = max;
972 | this.log(`End range automatically set to the max value of ${max}`);
973 | }
974 | if (start < 0 || end - start < 0 || start > max || end > max) {
975 | valid = false;
976 | this.log(`Invalid range: ${JSON.stringify(range)}`);
977 | this.opt.noMatch(range);
978 | } else if (string.substring(start, end).replace(/\s+/g, "") === "") {
979 | valid = false;
980 | this.log("Skipping whitespace only range: " + JSON.stringify(range));
981 | this.opt.noMatch(range);
982 | }
983 | return {
984 | start,
985 | end,
986 | valid
987 | };
988 | }
989 | /**
990 | * @typedef Mark~getTextNodesDict
991 | * @type {object.<string>}
992 | * @property {string} value - The composite value of all text nodes
993 | * @property {object[]} nodes - An array of objects
994 | * @property {number} nodes.start - The start position within the composite
995 | * value
996 | * @property {number} nodes.end - The end position within the composite
997 | * value
998 | * @property {HTMLElement} nodes.node - The DOM text node element
999 | */
1000 | /**
1001 | * Callback
1002 | * @callback Mark~getTextNodesCallback
1003 | * @param {Mark~getTextNodesDict}
1004 | */
1005 | /**
1006 | * Calls the callback with an object containing all text nodes (including
1007 | * iframe text nodes) with start and end positions and the composite value
1008 | * of them (string)
1009 | * @param {Mark~getTextNodesCallback} cb - Callback
1010 | * @access protected
1011 | */
1012 | getTextNodes(cb) {
1013 | let val = "", nodes = [];
1014 | this.iterator.forEachNode(NodeFilter.SHOW_TEXT, (node) => {
1015 | nodes.push({
1016 | start: val.length,
1017 | end: (val += node.textContent).length,
1018 | node
1019 | });
1020 | }, (node) => {
1021 | if (this.matchesExclude(node.parentNode)) {
1022 | return NodeFilter.FILTER_REJECT;
1023 | } else {
1024 | return NodeFilter.FILTER_ACCEPT;
1025 | }
1026 | }, () => {
1027 | cb({
1028 | value: val,
1029 | nodes
1030 | });
1031 | });
1032 | }
1033 | /**
1034 | * Checks if an element matches any of the specified exclude selectors. Also
1035 | * it checks for elements in which no marks should be performed (e.g.
1036 | * script and style tags) and optionally already marked elements
1037 | * @param {HTMLElement} el - The element to check
1038 | * @return {boolean}
1039 | * @access protected
1040 | */
1041 | matchesExclude(el) {
1042 | return DOMIterator.matches(el, this.opt.exclude.concat([
1043 | // ignores the elements itself, not their childrens (selector *)
1044 | "script",
1045 | "style",
1046 | "title",
1047 | "head",
1048 | "html"
1049 | ]));
1050 | }
1051 | /**
1052 | * Wraps the instance element and class around matches that fit the start
1053 | * and end positions within the node
1054 | * @param {HTMLElement} node - The DOM text node
1055 | * @param {number} start - The position where to start wrapping
1056 | * @param {number} end - The position where to end wrapping
1057 | * @return {HTMLElement} Returns the splitted text node that will appear
1058 | * after the wrapped text node
1059 | * @access protected
1060 | */
1061 | wrapRangeInTextNode(node, start, end) {
1062 | const hEl = !this.opt.element ? "mark" : this.opt.element, startNode = node.splitText(start), ret = startNode.splitText(end - start);
1063 | let repl = document.createElement(hEl);
1064 | repl.setAttribute("data-markjs", "true");
1065 | if (this.opt.className) {
1066 | repl.setAttribute("class", this.opt.className);
1067 | }
1068 | repl.textContent = startNode.textContent;
1069 | startNode.parentNode.replaceChild(repl, startNode);
1070 | return ret;
1071 | }
1072 | /**
1073 | * @typedef Mark~wrapRangeInMappedTextNodeDict
1074 | * @type {object.<string>}
1075 | * @property {string} value - The composite value of all text nodes
1076 | * @property {object[]} nodes - An array of objects
1077 | * @property {number} nodes.start - The start position within the composite
1078 | * value
1079 | * @property {number} nodes.end - The end position within the composite
1080 | * value
1081 | * @property {HTMLElement} nodes.node - The DOM text node element
1082 | */
1083 | /**
1084 | * Each callback
1085 | * @callback Mark~wrapMatchesEachCallback
1086 | * @param {HTMLElement} node - The wrapped DOM element
1087 | * @param {number} lastIndex - The last matching position within the
1088 | * composite value of text nodes
1089 | */
1090 | /**
1091 | * Filter callback
1092 | * @callback Mark~wrapMatchesFilterCallback
1093 | * @param {HTMLElement} node - The matching text node DOM element
1094 | */
1095 | /**
1096 | * Determines matches by start and end positions using the text node
1097 | * dictionary even across text nodes and calls
1098 | * {@link Mark#wrapRangeInTextNode} to wrap them
1099 | * @param {Mark~wrapRangeInMappedTextNodeDict} dict - The dictionary
1100 | * @param {number} start - The start position of the match
1101 | * @param {number} end - The end position of the match
1102 | * @param {Mark~wrapMatchesFilterCallback} filterCb - Filter callback
1103 | * @param {Mark~wrapMatchesEachCallback} eachCb - Each callback
1104 | * @access protected
1105 | */
1106 | wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) {
1107 | dict.nodes.every((n, i) => {
1108 | const sibl = dict.nodes[i + 1];
1109 | if (typeof sibl === "undefined" || sibl.start > start) {
1110 | if (!filterCb(n.node)) {
1111 | return false;
1112 | }
1113 | const s = start - n.start, e = (end > n.end ? n.end : end) - n.start, startStr = dict.value.substr(0, n.start), endStr = dict.value.substr(e + n.start);
1114 | n.node = this.wrapRangeInTextNode(n.node, s, e);
1115 | dict.value = startStr + endStr;
1116 | dict.nodes.forEach((k, j) => {
1117 | if (j >= i) {
1118 | if (dict.nodes[j].start > 0 && j !== i) {
1119 | dict.nodes[j].start -= e;
1120 | }
1121 | dict.nodes[j].end -= e;
1122 | }
1123 | });
1124 | end -= e;
1125 | eachCb(n.node.previousSibling, n.start);
1126 | if (end > n.end) {
1127 | start = n.end;
1128 | } else {
1129 | return false;
1130 | }
1131 | }
1132 | return true;
1133 | });
1134 | }
1135 | /**
1136 | * Filter callback before each wrapping
1137 | * @callback Mark~wrapMatchesFilterCallback
1138 | * @param {string} match - The matching string
1139 | * @param {HTMLElement} node - The text node where the match occurs
1140 | */
1141 | /**
1142 | * Callback for each wrapped element
1143 | * @callback Mark~wrapMatchesEachCallback
1144 | * @param {HTMLElement} element - The marked DOM element
1145 | */
1146 | /**
1147 | * Callback on end
1148 | * @callback Mark~wrapMatchesEndCallback
1149 | */
1150 | /**
1151 | * Wraps the instance element and class around matches within single HTML
1152 | * elements in all contexts
1153 | * @param {RegExp} regex - The regular expression to be searched for
1154 | * @param {number} ignoreGroups - A number indicating the amount of RegExp
1155 | * matching groups to ignore
1156 | * @param {Mark~wrapMatchesFilterCallback} filterCb
1157 | * @param {Mark~wrapMatchesEachCallback} eachCb
1158 | * @param {Mark~wrapMatchesEndCallback} endCb
1159 | * @access protected
1160 | */
1161 | wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) {
1162 | const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1163 | this.getTextNodes((dict) => {
1164 | dict.nodes.forEach((node) => {
1165 | node = node.node;
1166 | let match;
1167 | while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== "") {
1168 | if (!filterCb(match[matchIdx], node)) {
1169 | continue;
1170 | }
1171 | let pos = match.index;
1172 | if (matchIdx !== 0) {
1173 | for (let i = 1; i < matchIdx; i++) {
1174 | pos += match[i].length;
1175 | }
1176 | }
1177 | node = this.wrapRangeInTextNode(
1178 | node,
1179 | pos,
1180 | pos + match[matchIdx].length
1181 | );
1182 | eachCb(node.previousSibling);
1183 | regex.lastIndex = 0;
1184 | }
1185 | });
1186 | endCb();
1187 | });
1188 | }
1189 | /**
1190 | * Callback for each wrapped element
1191 | * @callback Mark~wrapMatchesAcrossElementsEachCallback
1192 | * @param {HTMLElement} element - The marked DOM element
1193 | */
1194 | /**
1195 | * Filter callback before each wrapping
1196 | * @callback Mark~wrapMatchesAcrossElementsFilterCallback
1197 | * @param {string} match - The matching string
1198 | * @param {HTMLElement} node - The text node where the match occurs
1199 | */
1200 | /**
1201 | * Callback on end
1202 | * @callback Mark~wrapMatchesAcrossElementsEndCallback
1203 | */
1204 | /**
1205 | * Wraps the instance element and class around matches across all HTML
1206 | * elements in all contexts
1207 | * @param {RegExp} regex - The regular expression to be searched for
1208 | * @param {number} ignoreGroups - A number indicating the amount of RegExp
1209 | * matching groups to ignore
1210 | * @param {Mark~wrapMatchesAcrossElementsFilterCallback} filterCb
1211 | * @param {Mark~wrapMatchesAcrossElementsEachCallback} eachCb
1212 | * @param {Mark~wrapMatchesAcrossElementsEndCallback} endCb
1213 | * @access protected
1214 | */
1215 | wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) {
1216 | const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1217 | this.getTextNodes((dict) => {
1218 | let match;
1219 | while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== "") {
1220 | let start = match.index;
1221 | if (matchIdx !== 0) {
1222 | for (let i = 1; i < matchIdx; i++) {
1223 | start += match[i].length;
1224 | }
1225 | }
1226 | const end = start + match[matchIdx].length;
1227 | this.wrapRangeInMappedTextNode(dict, start, end, (node) => {
1228 | return filterCb(match[matchIdx], node);
1229 | }, (node, lastIndex) => {
1230 | regex.lastIndex = lastIndex;
1231 | eachCb(node);
1232 | });
1233 | }
1234 | endCb();
1235 | });
1236 | }
1237 | /**
1238 | * Callback for each wrapped element
1239 | * @callback Mark~wrapRangeFromIndexEachCallback
1240 | * @param {HTMLElement} element - The marked DOM element
1241 | * @param {Mark~rangeObject} range - the current range object; provided
1242 | * start and length values will be numeric integers modified from the
1243 | * provided original ranges.
1244 | */
1245 | /**
1246 | * Filter callback before each wrapping
1247 | * @callback Mark~wrapRangeFromIndexFilterCallback
1248 | * @param {HTMLElement} node - The text node which includes the range
1249 | * @param {Mark~rangeObject} range - the current range object
1250 | * @param {string} match - string extracted from the matching range
1251 | * @param {number} counter - A counter indicating the number of all marks
1252 | */
1253 | /**
1254 | * Callback on end
1255 | * @callback Mark~wrapRangeFromIndexEndCallback
1256 | */
1257 | /**
1258 | * Wraps the indicated ranges across all HTML elements in all contexts
1259 | * @param {Mark~setOfRanges} ranges
1260 | * @param {Mark~wrapRangeFromIndexFilterCallback} filterCb
1261 | * @param {Mark~wrapRangeFromIndexEachCallback} eachCb
1262 | * @param {Mark~wrapRangeFromIndexEndCallback} endCb
1263 | * @access protected
1264 | */
1265 | wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) {
1266 | this.getTextNodes((dict) => {
1267 | const originalLength = dict.value.length;
1268 | ranges.forEach((range, counter) => {
1269 | let { start, end, valid } = this.checkWhitespaceRanges(
1270 | range,
1271 | originalLength,
1272 | dict.value
1273 | );
1274 | if (valid) {
1275 | this.wrapRangeInMappedTextNode(dict, start, end, (node) => {
1276 | return filterCb(
1277 | node,
1278 | range,
1279 | dict.value.substring(start, end),
1280 | counter
1281 | );
1282 | }, (node) => {
1283 | eachCb(node, range);
1284 | });
1285 | }
1286 | });
1287 | endCb();
1288 | });
1289 | }
1290 | /**
1291 | * Unwraps the specified DOM node with its content (text nodes or HTML)
1292 | * without destroying possibly present events (using innerHTML) and
1293 | * normalizes the parent at the end (merge splitted text nodes)
1294 | * @param {HTMLElement} node - The DOM node to unwrap
1295 | * @access protected
1296 | */
1297 | unwrapMatches(node) {
1298 | const parent = node.parentNode;
1299 | let docFrag = document.createDocumentFragment();
1300 | while (node.firstChild) {
1301 | docFrag.appendChild(node.removeChild(node.firstChild));
1302 | }
1303 | parent.replaceChild(docFrag, node);
1304 | if (!this.ie) {
1305 | parent.normalize();
1306 | } else {
1307 | this.normalizeTextNode(parent);
1308 | }
1309 | }
1310 | /**
1311 | * Normalizes text nodes. It's a workaround for the native normalize method
1312 | * that has a bug in IE (see attached link). Should only be used in IE
1313 | * browsers as it's slower than the native method.
1314 | * @see {@link http://tinyurl.com/z5asa8c}
1315 | * @param {HTMLElement} node - The DOM node to normalize
1316 | * @access protected
1317 | */
1318 | normalizeTextNode(node) {
1319 | if (!node) {
1320 | return;
1321 | }
1322 | if (node.nodeType === 3) {
1323 | while (node.nextSibling && node.nextSibling.nodeType === 3) {
1324 | node.nodeValue += node.nextSibling.nodeValue;
1325 | node.parentNode.removeChild(node.nextSibling);
1326 | }
1327 | } else {
1328 | this.normalizeTextNode(node.firstChild);
1329 | }
1330 | this.normalizeTextNode(node.nextSibling);
1331 | }
1332 | /**
1333 | * Callback when finished
1334 | * @callback Mark~commonDoneCallback
1335 | * @param {number} totalMatches - The number of marked elements
1336 | */
1337 | /**
1338 | * @typedef Mark~commonOptions
1339 | * @type {object.<string>}
1340 | * @property {string} [element="mark"] - HTML element tag name
1341 | * @property {string} [className] - An optional class name
1342 | * @property {string[]} [exclude] - An array with exclusion selectors.
1343 | * Elements matching those selectors will be ignored
1344 | * @property {boolean} [iframes=false] - Whether to search inside iframes
1345 | * @property {Mark~commonDoneCallback} [done]
1346 | * @property {boolean} [debug=false] - Wheter to log messages
1347 | * @property {object} [log=window.console] - Where to log messages (only if
1348 | * debug is true)
1349 | */
1350 | /**
1351 | * Callback for each marked element
1352 | * @callback Mark~markRegExpEachCallback
1353 | * @param {HTMLElement} element - The marked DOM element
1354 | */
1355 | /**
1356 | * Callback if there were no matches
1357 | * @callback Mark~markRegExpNoMatchCallback
1358 | * @param {RegExp} regexp - The regular expression
1359 | */
1360 | /**
1361 | * Callback to filter matches
1362 | * @callback Mark~markRegExpFilterCallback
1363 | * @param {HTMLElement} textNode - The text node which includes the match
1364 | * @param {string} match - The matching string for the RegExp
1365 | * @param {number} counter - A counter indicating the number of all marks
1366 | */
1367 | /**
1368 | * These options also include the common options from
1369 | * {@link Mark~commonOptions}
1370 | * @typedef Mark~markRegExpOptions
1371 | * @type {object.<string>}
1372 | * @property {Mark~markRegExpEachCallback} [each]
1373 | * @property {Mark~markRegExpNoMatchCallback} [noMatch]
1374 | * @property {Mark~markRegExpFilterCallback} [filter]
1375 | */
1376 | /**
1377 | * Marks a custom regular expression
1378 | * @param {RegExp} regexp - The regular expression
1379 | * @param {Mark~markRegExpOptions} [opt] - Optional options object
1380 | * @access public
1381 | */
1382 | markRegExp(regexp, opt) {
1383 | this.opt = opt;
1384 | this.log(`Searching with expression "${regexp}"`);
1385 | let totalMatches = 0, fn = "wrapMatches";
1386 | const eachCb = (element) => {
1387 | totalMatches++;
1388 | this.opt.each(element);
1389 | };
1390 | if (this.opt.acrossElements) {
1391 | fn = "wrapMatchesAcrossElements";
1392 | }
1393 | this[fn](regexp, this.opt.ignoreGroups, (match, node) => {
1394 | return this.opt.filter(node, match, totalMatches);
1395 | }, eachCb, () => {
1396 | if (totalMatches === 0) {
1397 | this.opt.noMatch(regexp);
1398 | }
1399 | this.opt.done(totalMatches);
1400 | });
1401 | }
1402 | /**
1403 | * Callback for each marked element
1404 | * @callback Mark~markEachCallback
1405 | * @param {HTMLElement} element - The marked DOM element
1406 | */
1407 | /**
1408 | * Callback if there were no matches
1409 | * @callback Mark~markNoMatchCallback
1410 | * @param {RegExp} term - The search term that was not found
1411 | */
1412 | /**
1413 | * Callback to filter matches
1414 | * @callback Mark~markFilterCallback
1415 | * @param {HTMLElement} textNode - The text node which includes the match
1416 | * @param {string} match - The matching term
1417 | * @param {number} totalCounter - A counter indicating the number of all
1418 | * marks
1419 | * @param {number} termCounter - A counter indicating the number of marks
1420 | * for the specific match
1421 | */
1422 | /**
1423 | * @typedef Mark~markAccuracyObject
1424 | * @type {object.<string>}
1425 | * @property {string} value - A accuracy string value
1426 | * @property {string[]} limiters - A custom array of limiters. For example
1427 | * <code>["-", ","]</code>
1428 | */
1429 | /**
1430 | * @typedef Mark~markAccuracySetting
1431 | * @type {string}
1432 | * @property {"partially"|"complementary"|"exactly"|Mark~markAccuracyObject}
1433 | * [accuracy="partially"] - Either one of the following string values:
1434 | * <ul>
1435 | * <li><i>partially</i>: When searching for "lor" only "lor" inside
1436 | * "lorem" will be marked</li>
1437 | * <li><i>complementary</i>: When searching for "lor" the whole word
1438 | * "lorem" will be marked</li>
1439 | * <li><i>exactly</i>: When searching for "lor" only those exact words
1440 | * will be marked. In this example nothing inside "lorem". This value
1441 | * is equivalent to the previous option <i>wordBoundary</i></li>
1442 | * </ul>
1443 | * Or an object containing two properties:
1444 | * <ul>
1445 | * <li><i>value</i>: One of the above named string values</li>
1446 | * <li><i>limiters</i>: A custom array of string limiters for accuracy
1447 | * "exactly" or "complementary"</li>
1448 | * </ul>
1449 | */
1450 | /**
1451 | * @typedef Mark~markWildcardsSetting
1452 | * @type {string}
1453 | * @property {"disabled"|"enabled"|"withSpaces"}
1454 | * [wildcards="disabled"] - Set to any of the following string values:
1455 | * <ul>
1456 | * <li><i>disabled</i>: Disable wildcard usage</li>
1457 | * <li><i>enabled</i>: When searching for "lor?m", the "?" will match zero
1458 | * or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When
1459 | * searching for "lor*m", the "*" will match zero or more non-space
1460 | * characters (e.g. "lorm", "loram", "lor123m", etc).</li>
1461 | * <li><i>withSpaces</i>: When searching for "lor?m", the "?" will
1462 | * match zero or one space or non-space character (e.g. "lor m", "loram",
1463 | * etc). When searching for "lor*m", the "*" will match zero or more space
1464 | * or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m",
1465 | * etc).</li>
1466 | * </ul>
1467 | */
1468 | /**
1469 | * @typedef Mark~markIgnorePunctuationSetting
1470 | * @type {string[]}
1471 | * @property {string} The strings in this setting will contain punctuation
1472 | * marks that will be ignored:
1473 | * <ul>
1474 | * <li>These punctuation marks can be between any characters, e.g. setting
1475 | * this option to <code>["'"]</code> would match "Worlds", "World's" and
1476 | * "Wo'rlds"</li>
1477 | * <li>One or more apostrophes between the letters would still produce a
1478 | * match (e.g. "W'o''r'l'd's").</li>
1479 | * <li>A typical setting for this option could be as follows:
1480 | * <pre>ignorePunctuation: ":;.,-–—‒_(){}[]!'\"+=".split(""),</pre> This
1481 | * setting includes common punctuation as well as a minus, en-dash,
1482 | * em-dash and figure-dash
1483 | * ({@link https://en.wikipedia.org/wiki/Dash#Figure_dash ref}), as well
1484 | * as an underscore.</li>
1485 | * </ul>
1486 | */
1487 | /**
1488 | * These options also include the common options from
1489 | * {@link Mark~commonOptions}
1490 | * @typedef Mark~markOptions
1491 | * @type {object.<string>}
1492 | * @property {boolean} [separateWordSearch=true] - Whether to search for
1493 | * each word separated by a blank instead of the complete term
1494 | * @property {boolean} [diacritics=true] - If diacritic characters should be
1495 | * matched. ({@link https://en.wikipedia.org/wiki/Diacritic Diacritics})
1496 | * @property {object} [synonyms] - An object with synonyms. The key will be
1497 | * a synonym for the value and the value for the key
1498 | * @property {Mark~markAccuracySetting} [accuracy]
1499 | * @property {Mark~markWildcardsSetting} [wildcards]
1500 | * @property {boolean} [acrossElements=false] - Whether to find matches
1501 | * across HTML elements. By default, only matches within single HTML
1502 | * elements will be found
1503 | * @property {boolean} [ignoreJoiners=false] - Whether to ignore word
1504 | * joiners inside of key words. These include soft-hyphens, zero-width
1505 | * space, zero-width non-joiners and zero-width joiners.
1506 | * @property {Mark~markIgnorePunctuationSetting} [ignorePunctuation]
1507 | * @property {Mark~markEachCallback} [each]
1508 | * @property {Mark~markNoMatchCallback} [noMatch]
1509 | * @property {Mark~markFilterCallback} [filter]
1510 | */
1511 | /**
1512 | * Marks the specified search terms
1513 | * @param {string|string[]} [sv] - Search value, either a search string or
1514 | * an array containing multiple search strings
1515 | * @param {Mark~markOptions} [opt] - Optional options object
1516 | * @access public
1517 | */
1518 | mark(sv, opt) {
1519 | this.opt = opt;
1520 | let totalMatches = 0, fn = "wrapMatches";
1521 | const {
1522 | keywords: kwArr,
1523 | length: kwArrLen
1524 | } = this.getSeparatedKeywords(typeof sv === "string" ? [sv] : sv), sens = this.opt.caseSensitive ? "" : "i", handler = (kw) => {
1525 | let regex = new RegExp(this.createRegExp(kw), `gm${sens}`), matches = 0;
1526 | this.log(`Searching with expression "${regex}"`);
1527 | this[fn](regex, 1, (term, node) => {
1528 | return this.opt.filter(node, kw, totalMatches, matches);
1529 | }, (element) => {
1530 | matches++;
1531 | totalMatches++;
1532 | this.opt.each(element);
1533 | }, () => {
1534 | if (matches === 0) {
1535 | this.opt.noMatch(kw);
1536 | }
1537 | if (kwArr[kwArrLen - 1] === kw) {
1538 | this.opt.done(totalMatches);
1539 | } else {
1540 | handler(kwArr[kwArr.indexOf(kw) + 1]);
1541 | }
1542 | });
1543 | };
1544 | if (this.opt.acrossElements) {
1545 | fn = "wrapMatchesAcrossElements";
1546 | }
1547 | if (kwArrLen === 0) {
1548 | this.opt.done(totalMatches);
1549 | } else {
1550 | handler(kwArr[0]);
1551 | }
1552 | }
1553 | /**
1554 | * Callback for each marked element
1555 | * @callback Mark~markRangesEachCallback
1556 | * @param {HTMLElement} element - The marked DOM element
1557 | * @param {array} range - array of range start and end points
1558 | */
1559 | /**
1560 | * Callback if a processed range is invalid, out-of-bounds, overlaps another
1561 | * range, or only matches whitespace
1562 | * @callback Mark~markRangesNoMatchCallback
1563 | * @param {Mark~rangeObject} range - a range object
1564 | */
1565 | /**
1566 | * Callback to filter matches
1567 | * @callback Mark~markRangesFilterCallback
1568 | * @param {HTMLElement} node - The text node which includes the range
1569 | * @param {array} range - array of range start and end points
1570 | * @param {string} match - string extracted from the matching range
1571 | * @param {number} counter - A counter indicating the number of all marks
1572 | */
1573 | /**
1574 | * These options also include the common options from
1575 | * {@link Mark~commonOptions}
1576 | * @typedef Mark~markRangesOptions
1577 | * @type {object.<string>}
1578 | * @property {Mark~markRangesEachCallback} [each]
1579 | * @property {Mark~markRangesNoMatchCallback} [noMatch]
1580 | * @property {Mark~markRangesFilterCallback} [filter]
1581 | */
1582 | /**
1583 | * Marks an array of objects containing a start with an end or length of the
1584 | * string to mark
1585 | * @param {Mark~setOfRanges} rawRanges - The original (preprocessed)
1586 | * array of objects
1587 | * @param {Mark~markRangesOptions} [opt] - Optional options object
1588 | * @access public
1589 | */
1590 | markRanges(rawRanges, opt) {
1591 | this.opt = opt;
1592 | let totalMatches = 0, ranges = this.checkRanges(rawRanges);
1593 | if (ranges && ranges.length) {
1594 | this.log(
1595 | "Starting to mark with the following ranges: " + JSON.stringify(ranges)
1596 | );
1597 | this.wrapRangeFromIndex(
1598 | ranges,
1599 | (node, range, match, counter) => {
1600 | return this.opt.filter(node, range, match, counter);
1601 | },
1602 | (element, range) => {
1603 | totalMatches++;
1604 | this.opt.each(element, range);
1605 | },
1606 | () => {
1607 | this.opt.done(totalMatches);
1608 | }
1609 | );
1610 | } else {
1611 | this.opt.done(totalMatches);
1612 | }
1613 | }
1614 | /**
1615 | * Removes all marked elements inside the context with their HTML and
1616 | * normalizes the parent at the end
1617 | * @param {Mark~commonOptions} [opt] - Optional options object
1618 | * @access public
1619 | */
1620 | unmark(opt) {
1621 | this.opt = opt;
1622 | let sel = this.opt.element ? this.opt.element : "*";
1623 | sel += "[data-markjs]";
1624 | if (this.opt.className) {
1625 | sel += `.${this.opt.className}`;
1626 | }
1627 | this.log(`Removal selector "${sel}"`);
1628 | this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, (node) => {
1629 | this.unwrapMatches(node);
1630 | }, (node) => {
1631 | const matchesSel = DOMIterator.matches(node, sel), matchesExclude = this.matchesExclude(node);
1632 | if (!matchesSel || matchesExclude) {
1633 | return NodeFilter.FILTER_REJECT;
1634 | } else {
1635 | return NodeFilter.FILTER_ACCEPT;
1636 | }
1637 | }, this.opt.done);
1638 | }
1639 | };
1640 |
1641 | // node_modules/mark.js/src/vanilla.js
1642 | function Mark2(ctx) {
1643 | const instance = new Mark(ctx);
1644 | this.mark = (sv, opt) => {
1645 | instance.mark(sv, opt);
1646 | return this;
1647 | };
1648 | this.markRegExp = (sv, opt) => {
1649 | instance.markRegExp(sv, opt);
1650 | return this;
1651 | };
1652 | this.markRanges = (sv, opt) => {
1653 | instance.markRanges(sv, opt);
1654 | return this;
1655 | };
1656 | this.unmark = (opt) => {
1657 | instance.unmark(opt);
1658 | return this;
1659 | };
1660 | return this;
1661 | }
1662 | export {
1663 | Mark2 as default
1664 | };
1665 | //# sourceMappingURL=vitepress___mark__js_src_vanilla__js.js.map
1666 |
```