[
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [\"master\"]\n  pull_request:\n    branches: [\"master\"]\n  schedule:\n    - cron: \"11 3 * * 3\"\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [javascript]\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v3\n        with:\n          languages: ${{ matrix.language }}\n          queries: +security-and-quality\n\n      - name: Autobuild\n        uses: github/codeql-action/autobuild@v3\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v3\n        with:\n          category: \"/language:${{ matrix.language }}\"\n"
  },
  {
    "path": ".github/workflows/coveralls.yml",
    "content": "name: Coveralls\n\non: [\"push\", \"pull_request\"]\n\njobs:\n  test:\n    name: Run units tests\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"22\"\n      - name: Install\n        run: npm install\n      - name: Install Playwright browsers\n        run: npx playwright install --with-deps chromium firefox\n      - name: Lint\n        run: npm run lint\n      - name: Test\n        run: npm test\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: \"38 20 * * 1\"\n  push:\n    branches: [\"master\"]\n\n# Declare default permissions as read only.\npermissions:\n  contents: read\n  actions: read\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n      contents: read\n      actions: read\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f\n        with:\n          name: sarif-results\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard (optional).\n      # Commenting out will disable upload of results to your repo's Code Scanning dashboard\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ncoverage\n.idea\n/playwright-report/\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npx pretty-quick --staged\nnpm run lint\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"arrowParens\": \"always\"\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Edwin Martin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Scorecard supply-chain security](https://github.com/edwinm/carbonium/actions/workflows/scorecard.yml/badge.svg)](https://github.com/edwinm/carbonium/actions/workflows/scorecard.yml) [![CodeQL](https://github.com/edwinm/carbonium/actions/workflows/codeql.yml/badge.svg)](https://github.com/edwinm/carbonium/actions/workflows/codeql.yml) [![Coverage Status](https://coveralls.io/repos/github/edwinm/carbonium/badge.svg?branch=master)](https://coveralls.io/github/edwinm/carbonium?branch=master) [![Socket Badge](https://socket.dev/api/badge/npm/package/carbonium)](https://socket.dev/npm/package/carbonium) [![CodeFactor](https://www.codefactor.io/repository/github/edwinm/carbonium/badge)](https://www.codefactor.io/repository/github/edwinm/carbonium) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=edwinm_carbonium&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=edwinm_carbonium) [![Snyk test results](https://snyk.io/test/github/edwinm/carbonium/badge.svg)](https://snyk.io/test/github/edwinm/carbonium) [![Size](https://badgen.net/bundlephobia/minzip/carbonium)](https://bundlephobia.com/package/carbonium) [![npm version](https://badge.fury.io/js/carbonium.svg)](https://www.npmjs.com/package/carbonium) [![GitHub](https://img.shields.io/github/license/edwinm/carbonium.svg)](https://github.com/edwinm/carbonium/blob/master/LICENSE) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/edwinm/carbonium?utm_source=oss&utm_medium=github&utm_campaign=edwinm%2Fcarbonium&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews)\n\n[![Carbonium](https://raw.githubusercontent.com/edwinm/carbonium/master/assets/carbonium.svg)](#readme)\n\n> One kilobyte library for easy DOM manipulation\n\nWith carbonium, you can call `$(selector)` and the result can be accessed as both an DOM element and an array of matched elements.\nDOM element operations are applied to all matched elements.\n\n## Examples\n\nTo set the `left` CSS property of all elements with the class `indent` to 40 pixels:\n\n```javascript\n$(\".indent\").style.left = \"40px\";\n```\n\nTo add the class `important` to all div's with \"priority\" as content:\n\n```javascript\n$(\"div\")\n  .filter((el) => el.textContent == \"priority\")\n  .classList.add(\"important\");\n```\n\nYou can use carbonium to create elements:\n\n```javascript\nconst error = $(\"<div class='error'>An error has occured!</div>\")[0];\n```\n\n## Installation\n\n```bash\nnpm install --save-dev carbonium\n```\n\nNow you can import carbonium:\n\n```javascript\nimport { $ } from \"carbonium\";\n```\n\nIf you don't want to install or use a bundler like webpack or rollup.js, you can import carbonium like this:\n\n```javascript\nconst { $ } = await import(\n  \"https://cdn.jsdelivr.net/npm/carbonium@1/dist/bundle.min.js\"\n);\n```\n\n## API\n\n### Select elements\n\n### `$(selector [, parentNode])`\n\n#### Parameters\n\n| Name       | Type                           | Description                                                                |\n| ---------- | ------------------------------ | -------------------------------------------------------------------------- |\n| selector   | string                         | Selector to select elements                                                |\n| parentNode | Document \\| Element (optional) | Document or element in which to apply the selector, defaults to `document` |\n\n#### Returns\n\nAn array of the matched elements, which can also be accessed as a single element.\n\n### Create element\n\n### `$(html [, parentNode])`\n\n#### Parameters\n\n| Name       | Type                           | Description                                                                |\n| ---------- | ------------------------------ | -------------------------------------------------------------------------- |\n| html       | string                         | HTML of element to create, starting with \"<\"                               |\n| parentNode | Document \\| Element (optional) | Document or element in which to apply the selector, defaults to `document` |\n\n#### Returns\n\nAn array with one created element.\n\n## TypeScript\n\nIf you use TypeScript, it's good to know Carbonium is written in TypeScript and provides all typings.\nYou can use generics to declare a specific type of element,\nfor example `HTMLInputElement` to make the `disabled` property available:\n\n```typescript\n$<HTMLInputElement>(\"input, select, button\").disabled = true;\n```\n\n## Why?\n\nYou might find most frameworks are quite bulky and bad for performance ([1](https://css-tricks.com/radeventlistener-a-tale-of-client-side-framework-performance/)).\nOn the other side, you might find using native DOM and writing `document.querySelectorAll(selector)` each time you want to do some DOM operations to become tedious.\nYou can write your own helper function, but that only takes part of the pain away.\n\nCarbonium seeks to find the sweet spot between using a framework and using the native DOM.\n\n## jQuery\n\nIsn't this just jQuery and isn't that obsolete and bad practice?\n\nNo. Carbonium doesn't have the disadvantages of jQuery:\n\n1. Carbonium is very small: just around one kilobyte.\n2. There's no new API to learn, carbonium provides only standard DOM API's.\n\n## Browser support\n\nCarbonium is supported by all modern browsers. It is tested to work on desktop and mobile with Firefox 79, Chrome 84, Safari 13 and Edge 84.\nIt should work with all browsers supporting Proxy, see [Can I use Proxy](https://caniuse.com/#feat=proxy) for support tables.\n\n## Name\n\n[<img src=\"https://raw.githubusercontent.com/edwinm/carbonium/master/assets/Diamond_and_graphite.jpg\" align=\"right\"\n     alt=\"Photo of diamond and graphite\" width=\"178\" height=\"120\">](https://commons.wikimedia.org/wiki/File:Diamond_and_graphite_without_structures.jpg)\n\nCarbonium is the Latin name for carbon. Carbon has two forms (allotropes): graphite and diamond.\nJust like this library, in which the result presents itself both as one element and as a list of elements.\n\n[Photo CC BY-SA 3.0](https://commons.wikimedia.org/wiki/File:Diamond_and_graphite_without_structures.jpg)\n\n## License\n\nCopyright 2023 [Edwin Martin](https://bitstorm.org/) and released under the MIT license.\n"
  },
  {
    "path": "demo/demo.js",
    "content": "const importPromise = import(\n  \"https://cdn.jsdelivr.net/npm/carbonium/dist/bundle.min.js\"\n);\n\nconst loadPromise = new Promise((resolve) => {\n  document.addEventListener(\"DOMContentLoaded\", resolve);\n});\n\n// Start code when both carbonium and the page are loaded\nPromise.all([importPromise, loadPromise]).then(([{ $ }]) => {\n  $(\"#out\").innerText = \"Demo.\";\n  $(\"#hello-button\").addEventListener(\"click\", () => {\n    $(\"#out\").innerText = \"Hello. It is working!\";\n  });\n});\n"
  },
  {
    "path": "demo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <title>Demo</title>\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1\"\n    />\n    <script src=\"demo.js\"></script>\n  </head>\n  <body>\n    <h1>Demo</h1>\n\n    <div id=\"out\">…</div>\n\n    <button id=\"hello-button\">Say hello</button>\n  </body>\n</html>\n"
  },
  {
    "path": "dist/bundle.js",
    "content": "/**\n Carbonium 1.3.0\n @copyright 2020 Edwin Martin\n @license MIT\n */\nfunction $(selectors, parentNode) {\n    let nodelist;\n    // If the first parameter starts with \"<\", create a DOM node\n    if (selectors.startsWith(\"<\")) {\n        nodelist = [\n            new DOMParser().parseFromString(selectors, \"text/html\").body.firstChild,\n        ];\n    }\n    else {\n        // Else, do querySelectorAll\n        nodelist = (parentNode || document).querySelectorAll(selectors);\n    }\n    // Wrap it in a Proxy\n    return new Proxy(nodelist, proxyHandler);\n}\n// Used by style, classList and relList\n// When setting one of these, remember the elements to apply to\nlet currentListNodelist;\nlet propList;\nconst proxyHandler = {\n    get(target, prop) {\n        let propValue = null;\n        // Return iterator when asked for iterator, only used in ES2015+\n        if (prop == Symbol.iterator) {\n            return function* () {\n                for (const element of target) {\n                    yield element;\n                }\n            };\n        }\n        // Special case for style, classList and relList\n        if (prop == \"style\" || prop == \"classList\" || prop == \"relList\") {\n            currentListNodelist = target;\n            propList = prop;\n            // Matched elements can be a list of any element or an empty list\n            // Use getter of, for example, document.body.style\n            const propValue = Reflect.get(document.body, prop);\n            return new Proxy(propValue, proxyHandler);\n        }\n        // style.setProperty, getPropertyValue…, classList.add, contains, remove…, relList…\n        if (target instanceof CSSStyleDeclaration ||\n            target instanceof DOMTokenList) {\n            // Matched elements can be a list of any element or an empty list\n            // Use getter of, for example, document.body.style.color\n            propValue = Reflect.get(document.body[propList], prop);\n            // When getter is a function, apply it to all matched elements\n            if (typeof propValue == \"function\") {\n                return new Proxy(propValue, {\n                    apply: function (target, thisArg, argumentsList) {\n                        currentListNodelist.forEach((el) => {\n                            Reflect.apply(target, el[propList], argumentsList);\n                        });\n                        return new Proxy(currentListNodelist, proxyHandler);\n                    },\n                });\n            }\n            else {\n                return propValue;\n            }\n        }\n        // Are we dealing with an Array function like forEach, map and filter?\n        if (Array.prototype.hasOwnProperty(prop)) {\n            const propValue = Reflect.get(Array.prototype, prop);\n            if (typeof propValue == \"function\") {\n                return new Proxy(propValue, {\n                    apply: function (target, thisArg, argumentsList) {\n                        const ret = Reflect.apply(target, thisArg, argumentsList);\n                        // When function returns undefined (like forEach),\n                        // return all matched elements, so calls can be chained\n                        // For example forEach(…).setAttribute(…)\n                        const newTarget = typeof ret != \"undefined\" ? ret : thisArg;\n                        return new Proxy(newTarget, proxyHandler);\n                    },\n                });\n            }\n        }\n        // Get property or call function on DOM elements\n        if (target.length > 0) {\n            // Might be DOM element specific, like input.select(),\n            // so use first array element to get reference\n            if (prop in target[0]) {\n                propValue = Reflect.get(target[0], prop);\n            }\n        }\n        else {\n            // Empty list, targeted DOM element unknown,\n            // use getter of document.body\n            if (prop in document.body) {\n                propValue = Reflect.get(document.body, prop);\n            }\n        }\n        // Propagate DOM prop value\n        if (propValue) {\n            if (typeof propValue == \"function\") {\n                return new Proxy(propValue, {\n                    apply: function (target, thisArg, argumentsList) {\n                        let retFirst = null;\n                        let first = true;\n                        // Apply on individual elements\n                        for (const el of thisArg) {\n                            const ret = Reflect.apply(target, el, argumentsList);\n                            if (first) {\n                                retFirst = ret;\n                                first = false;\n                            }\n                        }\n                        return retFirst !== null && retFirst !== void 0 ? retFirst : thisArg;\n                    },\n                });\n            }\n            else {\n                return propValue;\n            }\n        }\n        // Default\n        return Reflect.get(target, prop);\n    },\n    // DOM property is set\n    set(target, prop, value) {\n        if (\"forEach\" in target && !(target instanceof CSSStyleDeclaration)) {\n            target.forEach((el) => {\n                Reflect.set(el, prop, value);\n            });\n        }\n        else {\n            Reflect.set(target, prop, value);\n        }\n        return true;\n    },\n    deleteProperty(target, prop) {\n        if (prop in target) {\n            return delete target[prop];\n        }\n        return false;\n    },\n};\n\nexport { $ };\n//# sourceMappingURL=bundle.js.map\n"
  },
  {
    "path": "dist/carbonium.d.ts",
    "content": "/**\n Carbonium __buildVersion__\n @copyright 2020 Edwin Martin\n @license MIT\n */\nexport declare function $<T extends HTMLElement = HTMLElement>(selectors: string, parentNode?: Document | ShadowRoot | HTMLElement): CarboniumType<T>;\nexport type CarboniumType<T extends HTMLElement = HTMLElement> = CarboniumList<T> & T;\ninterface CarboniumList<T extends HTMLElement> extends Array<T> {\n    concat(...items: ConcatArray<T>[]): CarboniumType<T>;\n    concat(...items: (T | ConcatArray<T>)[]): CarboniumType<T>;\n    reverse(): CarboniumType<T>;\n    slice(start?: number, end?: number): CarboniumType<T>;\n    splice(start: number, deleteCount?: number): CarboniumType<T>;\n    splice(start: number, deleteCount: number, ...items: T[]): CarboniumType<T>;\n    forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): CarboniumType<T>;\n    filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): CarboniumType<T>;\n    setAttribute(qualifiedName: string, value: string): CarboniumType<T>;\n    classList: CarboniumClassList<T>;\n    style: CarboniumStyleList<T>;\n}\ninterface CarboniumClassList<T extends HTMLElement> extends DOMTokenList {\n    add(...tokens: string[]): CarboniumType<T>;\n    remove(...tokens: string[]): CarboniumType<T>;\n    replace(oldToken: string, newToken: string): boolean;\n    forEach(callbackfn: (value: string, key: number, parent: DOMTokenList) => void, thisArg?: any): CarboniumType<T>;\n}\ninterface CarboniumStyleList<T extends HTMLElement> extends CSSStyleDeclaration {\n    removeProperty(property: string): CarboniumList<T> & string;\n    setProperty(property: string, value: string | null, priority?: string): CarboniumType<T>;\n}\nexport {};\n"
  },
  {
    "path": "dist/src/carbonium.d.ts",
    "content": "/**\n Carbonium __buildVersion__\n @copyright 2020 Edwin Martin\n @license MIT\n */\nexport declare function $<T extends HTMLElement = HTMLElement>(selectors: string, parentNode?: Document | ShadowRoot | HTMLElement): CarboniumType<T>;\nexport type CarboniumType<T extends HTMLElement = HTMLElement> = CarboniumList<T> & T;\ninterface CarboniumList<T extends HTMLElement> extends Array<T> {\n    concat(...items: ConcatArray<T>[]): CarboniumType<T>;\n    concat(...items: (T | ConcatArray<T>)[]): CarboniumType<T>;\n    reverse(): CarboniumType<T>;\n    slice(start?: number, end?: number): CarboniumType<T>;\n    splice(start: number, deleteCount?: number): CarboniumType<T>;\n    splice(start: number, deleteCount: number, ...items: T[]): CarboniumType<T>;\n    forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): CarboniumType<T>;\n    filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): CarboniumType<T>;\n    setAttribute(qualifiedName: string, value: string): CarboniumType<T>;\n    classList: CarboniumClassList<T>;\n    style: CarboniumStyleList<T>;\n}\ninterface CarboniumClassList<T extends HTMLElement> extends DOMTokenList {\n    add(...tokens: string[]): CarboniumType<T>;\n    remove(...tokens: string[]): CarboniumType<T>;\n    replace(oldToken: string, newToken: string): boolean;\n    forEach(callbackfn: (value: string, key: number, parent: DOMTokenList) => void, thisArg?: any): CarboniumType<T>;\n}\ninterface CarboniumStyleList<T extends HTMLElement> extends CSSStyleDeclaration {\n    removeProperty(property: string): CarboniumList<T> & string;\n    setProperty(property: string, value: string | null, priority?: string): CarboniumType<T>;\n}\nexport {};\n"
  },
  {
    "path": "eslint.config.js",
    "content": "import tseslint from \"typescript-eslint\";\nimport js from \"@eslint/js\";\nimport prettier from \"eslint-config-prettier\";\nimport globals from \"globals\";\n\nexport default tseslint.config(\n  {\n    ignores: [\n      \"dist/**\",\n      \"demo/**\",\n      \"coverage/**\",\n      \"iteratortest/**\",\n      \"karma.conf.cjs\",\n    ],\n  },\n  js.configs.recommended,\n  ...tseslint.configs.recommended,\n  {\n    languageOptions: {\n      globals: {\n        ...globals.browser,\n        ...globals.es2021,\n      },\n    },\n    rules: {\n      \"no-prototype-builtins\": \"off\",\n      \"@typescript-eslint/no-explicit-any\": \"off\",\n      \"@typescript-eslint/no-unsafe-function-type\": \"off\",\n    },\n  },\n  prettier\n);\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"carbonium\",\n  \"version\": \"1.3.0\",\n  \"description\": \"One kilobyte library for easy DOM manipulation\",\n  \"type\": \"module\",\n  \"browser\": \"dist/bundle.iife.min.js\",\n  \"module\": \"dist/bundle.min.js\",\n  \"types\": \"dist/carbonium.d.ts\",\n  \"sideEffects\": false,\n  \"scripts\": {\n    \"prepare\": \"husky\",\n    \"start\": \"http-server -o demo/ --silent\",\n    \"build\": \"rollup --config --sourcemap\",\n    \"dev\": \"rollup --config --sourcemap --watch\",\n    \"release\": \"npm i --package-lock && npm run lint && npm test && npm publish\",\n    \"pretest\": \"npm run build\",\n    \"test\": \"playwright test\",\n    \"lint\": \"npx eslint .\",\n    \"prettier\": \"prettier --config .prettierrc.json src/**/*.ts *.json --write\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/edwinm/carbonium.git\"\n  },\n  \"files\": [\n    \"src/carbonium.ts\",\n    \"dist/carbonium.d.ts\",\n    \"dist/bundle.min.js\",\n    \"dist/bundle.min.js.map\",\n    \"dist/bundle.iife.min.js\",\n    \"dist/bundle.iife.min.js.map\"\n  ],\n  \"keywords\": [\n    \"front-end\",\n    \"dom\",\n    \"qsa\",\n    \"jquery\",\n    \"typescript\",\n    \"front-end\",\n    \"lightweight\",\n    \"micro\"\n  ],\n  \"author\": {\n    \"name\": \"Edwin Martin\",\n    \"email\": \"edwin@bitstorm.org\",\n    \"url\": \"https://bitstorm.org/\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/edwinm/carbonium/issues\"\n  },\n  \"homepage\": \"https://github.com/edwinm/carbonium#readme\",\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.39.4\",\n    \"@playwright/test\": \"^1.59.1\",\n    \"@rollup/plugin-replace\": \"^6.0.3\",\n    \"@rollup/plugin-terser\": \"^1.0.0\",\n    \"@rollup/plugin-typescript\": \"^12.3.0\",\n    \"@types/node\": \"^22.19.17\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.58.0\",\n    \"@typescript-eslint/parser\": \"^8.58.0\",\n    \"eslint\": \"^9.39.4\",\n    \"eslint-config-prettier\": \"^10.1.8\",\n    \"globals\": \"^17.4.0\",\n    \"http-server\": \"^14.1.1\",\n    \"husky\": \"^9.1.7\",\n    \"prettier\": \"^3.8.1\",\n    \"pretty-quick\": \"^4.2.2\",\n    \"rollup\": \"^4.60.1\",\n    \"tslib\": \"^2.8.1\",\n    \"typescript\": \"^6.0.2\",\n    \"typescript-eslint\": \"^8.58.0\"\n  }\n}\n"
  },
  {
    "path": "playwright.config.ts",
    "content": "import { defineConfig, devices } from \"@playwright/test\";\n\nexport default defineConfig({\n  testDir: \"./tests\",\n  fullyParallel: true,\n  retries: 0,\n  reporter: [[\"html\", { open: \"never\" }]],\n  projects: [\n    { name: \"chromium\", use: { ...devices[\"Desktop Chrome\"] } },\n    { name: \"firefox\", use: { ...devices[\"Desktop Firefox\"] } },\n  ],\n});\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import { createRequire } from \"module\";\nimport typescript from \"@rollup/plugin-typescript\";\nimport terser from \"@rollup/plugin-terser\";\nimport replace from \"@rollup/plugin-replace\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"./package.json\");\n\nexport default {\n  input: \"src/carbonium.ts\",\n  output: [\n    {\n      file: \"dist/bundle.min.js\",\n      format: \"es\",\n      name: \"bundle\",\n      sourcemap: true,\n      plugins: [terser()],\n    },\n    {\n      file: \"dist/bundle.js\",\n      format: \"es\",\n      name: \"bundle\",\n      sourcemap: true,\n    },\n    {\n      file: \"dist/bundle.iife.min.js\",\n      format: \"iife\",\n      name: \"carbonium\",\n      sourcemap: true,\n      plugins: [terser()],\n    },\n  ],\n  plugins: [\n    replace({\n      preventAssignment: false,\n      __buildVersion__: pkg.version,\n    }),\n    typescript({ declarationDir: \"dist\" }),\n  ],\n};\n"
  },
  {
    "path": "src/carbonium.ts",
    "content": "/**\n Carbonium __buildVersion__\n @copyright 2020 Edwin Martin\n @license MIT\n */\n\nexport function $<T extends HTMLElement = HTMLElement>(\n  selectors: string,\n  parentNode?: Document | ShadowRoot | HTMLElement\n): CarboniumType<T> {\n  let nodelist: NodeListOf<T>;\n\n  // If the first parameter starts with \"<\", create a DOM node\n  if (selectors.startsWith(\"<\")) {\n    nodelist = <NodeListOf<T>>(\n      (<unknown>[\n        new DOMParser().parseFromString(selectors, \"text/html\").body.firstChild,\n      ])\n    );\n  } else {\n    // Else, do querySelectorAll\n    nodelist = (parentNode || document).querySelectorAll(selectors);\n  }\n\n  // Wrap it in a Proxy\n  return <CarboniumType<T>>(\n    (<unknown>new Proxy<NodeListOf<T>>(nodelist, proxyHandler))\n  );\n}\n\n// Used by style, classList and relList\n// When setting one of these, remember the elements to apply to\nlet currentListNodelist: NodeListOf<HTMLElement>;\nlet propList: \"style\" | \"classList\" | \"relList\";\n\nconst proxyHandler: ProxyHandler<NodeListOf<HTMLElement>> = {\n  get(target, prop) {\n    let propValue = null;\n\n    // Return iterator when asked for iterator, only used in ES2015+\n    if (prop == Symbol.iterator) {\n      return function* () {\n        for (const element of target) {\n          yield element;\n        }\n      };\n    }\n\n    // Special case for style, classList and relList\n    if (prop == \"style\" || prop == \"classList\" || prop == \"relList\") {\n      currentListNodelist = target;\n      propList = prop;\n      // Matched elements can be a list of any element or an empty list\n      // Use getter of, for example, document.body.style\n      const propValue = Reflect.get(document.body, prop);\n      return new Proxy(propValue, proxyHandler);\n    }\n\n    // style.setProperty, getPropertyValue…, classList.add, contains, remove…, relList…\n    if (\n      target instanceof CSSStyleDeclaration ||\n      target instanceof DOMTokenList\n    ) {\n      // Matched elements can be a list of any element or an empty list\n      // Use getter of, for example, document.body.style.color\n      propValue = Reflect.get((document.body as any)[propList], prop);\n\n      // When getter is a function, apply it to all matched elements\n      if (typeof propValue == \"function\") {\n        return new Proxy<Function>(propValue, {\n          apply: function (target, thisArg, argumentsList) {\n            currentListNodelist.forEach((el) => {\n              Reflect.apply(target, (el as any)[propList], argumentsList);\n            });\n            return new Proxy(currentListNodelist, proxyHandler);\n          },\n        });\n      } else {\n        return propValue;\n      }\n    }\n\n    // Are we dealing with an Array function like forEach, map and filter?\n    if (Array.prototype.hasOwnProperty(prop)) {\n      const propValue = Reflect.get(Array.prototype, prop);\n      if (typeof propValue == \"function\") {\n        return new Proxy<Function>(propValue, {\n          apply: function (target, thisArg, argumentsList) {\n            const ret = Reflect.apply(target, thisArg, argumentsList);\n            // When function returns undefined (like forEach),\n            // return all matched elements, so calls can be chained\n            // For example forEach(…).setAttribute(…)\n            const newTarget = typeof ret != \"undefined\" ? ret : thisArg;\n            return new Proxy(newTarget, proxyHandler);\n          },\n        });\n      }\n    }\n\n    // Get property or call function on DOM elements\n    if (target.length > 0) {\n      // Might be DOM element specific, like input.select(),\n      // so use first array element to get reference\n      if (prop in target[0]) {\n        propValue = Reflect.get(target[0], prop);\n      }\n    } else {\n      // Empty list, targeted DOM element unknown,\n      // use getter of document.body\n      if (prop in document.body) {\n        propValue = Reflect.get(document.body, prop);\n      }\n    }\n\n    // Propagate DOM prop value\n    if (propValue) {\n      if (typeof propValue == \"function\") {\n        return new Proxy<Function>(propValue, {\n          apply: function (target, thisArg, argumentsList) {\n            let retFirst = null;\n            let first = true;\n            // Apply on individual elements\n            for (const el of thisArg) {\n              const ret = Reflect.apply(target, el, argumentsList);\n              if (first) {\n                retFirst = ret;\n                first = false;\n              }\n            }\n            return retFirst ?? thisArg;\n          },\n        });\n      } else {\n        return propValue;\n      }\n    }\n\n    // Default\n    return Reflect.get(target, prop);\n  },\n\n  // DOM property is set\n  set(target, prop, value) {\n    if (\"forEach\" in target && !(target instanceof CSSStyleDeclaration)) {\n      target.forEach((el) => {\n        Reflect.set(el, prop, value);\n      });\n    } else {\n      Reflect.set(target, prop, value);\n    }\n    return true;\n  },\n\n  deleteProperty(target, prop) {\n    if (prop in target) {\n      return delete (target as any)[prop];\n    }\n    return false;\n  },\n};\n\nexport type CarboniumType<T extends HTMLElement = HTMLElement> =\n  CarboniumList<T> & T;\n\n// Interface definitions\n\ninterface CarboniumList<T extends HTMLElement> extends Array<T> {\n  concat(...items: ConcatArray<T>[]): CarboniumType<T>;\n\n  concat(...items: (T | ConcatArray<T>)[]): CarboniumType<T>;\n\n  reverse(): CarboniumType<T>;\n\n  slice(start?: number, end?: number): CarboniumType<T>;\n\n  splice(start: number, deleteCount?: number): CarboniumType<T>;\n\n  /* tslint:disable:unified-signatures */\n  splice(start: number, deleteCount: number, ...items: T[]): CarboniumType<T>;\n\n  forEach(\n    callbackfn: (value: T, index: number, array: T[]) => void,\n    thisArg?: any\n  ): CarboniumType<T>;\n\n  filter(\n    callbackfn: (value: T, index: number, array: T[]) => boolean,\n    thisArg?: any\n  ): CarboniumType<T>;\n\n  setAttribute(qualifiedName: string, value: string): CarboniumType<T>;\n\n  classList: CarboniumClassList<T>;\n  style: CarboniumStyleList<T>;\n}\n\ninterface CarboniumClassList<T extends HTMLElement> extends DOMTokenList {\n  add(...tokens: string[]): CarboniumType<T>;\n\n  remove(...tokens: string[]): CarboniumType<T>;\n\n  replace(oldToken: string, newToken: string): boolean;\n\n  forEach(\n    callbackfn: (value: string, key: number, parent: DOMTokenList) => void,\n    thisArg?: any\n  ): CarboniumType<T>;\n}\n\ninterface CarboniumStyleList<\n  T extends HTMLElement,\n> extends CSSStyleDeclaration {\n  removeProperty(property: string): CarboniumList<T> & string;\n\n  setProperty(\n    property: string,\n    value: string | null,\n    priority?: string\n  ): CarboniumType<T>;\n}\n"
  },
  {
    "path": "test/test.ts",
    "content": "import * as assert from \"assert\";\nimport { $, CarboniumType } from \"../src/carbonium\";\n// TODO: this should work - find out why not\n// import { $, CarboniumType } from \"../\";\nimport \"@webcomponents/webcomponentsjs/custom-elements-es5-adapter\";\n\n/**\n * Test framework used:\n * Mocha https://mochajs.org/\n * Assert https://nodejs.org/api/assert.html\n */\n\ndescribe(\"$\", () => {\n  beforeEach(() => {\n    document.body.textContent = \"\";\n    for (let i = 0; i < 6; i++) {\n      const div = document.createElement(\"div\");\n      div.textContent = `item${i}`;\n      document.body.appendChild(div);\n    }\n  });\n\n  it(\"textContent one element\", () => {\n    $(\"div:first-child\").textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].textContent, \"hello\");\n  });\n\n  it(\"textContent one element\", () => {\n    const div: CarboniumType = $(\"div:first-child\");\n    div.textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].textContent, \"hello\");\n  });\n\n  it(\"textContent all elements\", () => {\n    $(\"div\").textContent = \"hello\";\n    assert.equal(document.body.textContent, \"hellohellohellohellohellohello\");\n  });\n\n  it(\"length\", () => {\n    assert.equal($(\"div\").length, 6);\n  });\n\n  it(\"forEach\", () => {\n    const divs = $(\"div\");\n    divs.forEach((div, i) => {\n      div.textContent = `div ${i}`;\n    });\n    assert.equal(divs[0].textContent, \"div 0\");\n    assert.equal(divs[5].textContent, \"div 5\");\n  });\n\n  it(\"for of\", () => {\n    const divs = $(\"div\");\n    let i = 0;\n    for (const div of divs) {\n      div.textContent = `div ${i++}`;\n    }\n    assert.equal(divs[0].textContent, \"div 0\");\n    assert.equal(divs[5].textContent, \"div 5\");\n  });\n\n  it(\"setAttribute all elements\", () => {\n    $(\"div\").setAttribute(\"aria-label\", \"List item\");\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].getAttribute(\"aria-label\"), \"List item\");\n    assert.equal(divs[1].getAttribute(\"aria-label\"), \"List item\");\n    assert.equal(divs[5].getAttribute(\"aria-label\"), \"List item\");\n  });\n\n  it(\"filter\", () => {\n    $(\"div\").filter((el) => el.textContent == \"item1\").textContent = \"hello\";\n    assert.equal(document.body.textContent, \"item0helloitem2item3item4item5\");\n  });\n\n  it(\"class add method\", () => {\n    $(\"div\").classList.add(\"some-class\");\n    const divs = document.getElementsByTagName(\"div\");\n    assert.ok(divs[0].classList.contains(\"some-class\"));\n    assert.ok(divs[5].classList.contains(\"some-class\"));\n  });\n\n  it(\"rel add and contains method\", () => {\n    const a = document.createElement(\"a\");\n    a.relList.add(\"some-class\");\n    assert.ok(a.relList.contains(\"some-class\"));\n  });\n\n  it(\"class value property\", () => {\n    $(\"div\").classList.add(\"some-class\");\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].classList.value, \"some-class\");\n  });\n\n  it(\"class add method and textContent property\", () => {\n    $(\"div:first-child\").classList.add(\"some-class\").textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.ok(divs[0].classList.contains(\"some-class\"));\n    assert.ok(!divs[5].classList.contains(\"some-class\"));\n    assert.equal(divs[0].textContent, \"hello\");\n    assert.equal(divs[5].textContent, \"item5\");\n  });\n\n  it(\"filter and class add method and textContent property\", () => {\n    $(\"div\")\n      .filter((el) => el.textContent == \"item0\")\n      .classList.add(\"some-class\").textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.ok(divs[0].classList.contains(\"some-class\"));\n    assert.ok(!divs[5].classList.contains(\"some-class\"));\n    assert.equal(divs[0].textContent, \"hello\");\n    assert.equal(divs[5].textContent, \"item5\");\n  });\n\n  it(\"filter and style setProperty method and textContent property\", () => {\n    $(\"div\")\n      .filter((el) => el.textContent == \"item0\")\n      .style.setProperty(\"--leftmargin\", \"10px\").textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].style.getPropertyValue(\"--leftmargin\"), \"10px\");\n    assert.equal(divs[5].style.getPropertyValue(\"--leftmargin\"), \"\");\n    assert.equal(divs[0].textContent, \"hello\");\n    assert.equal(divs[5].textContent, \"item5\");\n  });\n\n  it(\"combined\", () => {\n    $(\"div\")\n      .forEach((el) => (el.title = `A div with content ${el.textContent}`))\n      .setAttribute(\"aria-label\", \"List item\")\n      .filter((el) => el.textContent == \"item1\").textContent = \"hello\";\n    const divs = document.getElementsByTagName(\"div\");\n    assert.equal(divs[0].getAttribute(\"aria-label\"), \"List item\");\n    assert.equal(divs[5].getAttribute(\"aria-label\"), \"List item\");\n    assert.equal(divs[0].getAttribute(\"title\"), \"A div with content item0\");\n    assert.equal(divs[5].getAttribute(\"title\"), \"A div with content item5\");\n    assert.equal(document.body.textContent, \"item0helloitem2item3item4item5\");\n  });\n\n  it(\"textContent empty list\", () => {\n    assert.doesNotThrow(() => {\n      $(\"div.non-existent\").textContent = \"hello\";\n    });\n  });\n\n  it(\"setAttribute empty list\", () => {\n    assert.doesNotThrow(() => {\n      $(\"div.non-existent\").setAttribute(\"aria-label\", \"List item\");\n    });\n  });\n\n  it(\"call element specific function\", () => {\n    const input = document.createElement(\"input\");\n    document.querySelector(\"div:first-child\").appendChild(input);\n    assert.doesNotThrow(() => {\n      $<HTMLInputElement>(\"input\").select();\n    });\n  });\n\n  it(\"addEventListener\", (done) => {\n    $(\"div:first-child\").addEventListener(\"click\", () => {\n      done();\n    });\n    $(\"div:first-child\").click();\n  });\n\n  it(\"canvas\", () => {\n    const canvas = document.createElement(\"canvas\");\n    $(\"div:nth-child(1)\").appendChild(canvas);\n    const ctx = $<HTMLCanvasElement>(\"canvas\").getContext(\"2d\", {\n      alpha: false,\n    });\n    ctx.fillRect(0, 0, 100, 100);\n  });\n\n  it(\"style set/get\", () => {\n    $(\"div:nth-child(1)\").style.color = \"red\";\n    assert.equal($(\"div:nth-child(1)\").style.color, \"red\");\n  });\n\n  it(\"Parse HTML\", () => {\n    const div$ = $(\"<div class='a1'>b1</div>\");\n    assert.ok(div$.classList.contains(\"a1\"));\n    $(\"div:first-child\").appendChild(div$[0]);\n    assert.equal($(\".a1\").length, 1);\n    assert.equal($(\".a1\").textContent, \"b1\");\n  });\n\n  it(\"Set\", () => {\n    const set = new Set([\"1a\", \"2a\", \"3a\"]);\n    let result = \"\";\n    set.forEach((item) => {\n      result += `[${item}]`;\n    });\n    assert.equal(result, \"[1a][2a][3a]\");\n  });\n  it(\"Custom Element\", () => {\n    class GolInfo extends HTMLElement {\n      connectedCallback() {\n        $(\"nnn\").addEventListener(\"click\", () => {\n          console.log(\"click\");\n        });\n      }\n    }\n    customElements.define(\"gol-info\", GolInfo);\n\n    const i = document.createElement(\"gol-info\");\n    // const i = new GolInfo();\n    document.body.appendChild(i);\n  });\n});\n"
  },
  {
    "path": "tests/carbonium.spec.ts",
    "content": "import { test, expect, Page } from \"@playwright/test\";\nimport path from \"path\";\n\ndeclare global {\n  function $(\n    selector: string,\n    parent?: Document | ShadowRoot | HTMLElement\n  ): any;\n}\n\nconst bundlePath = path.resolve(\"./dist/bundle.iife.min.js\");\n\nasync function checkFirstClassAndText(page: Page) {\n  const results = await page.evaluate(() => {\n    const divs = document.getElementsByTagName(\"div\");\n    return [\n      divs[0].classList.contains(\"some-class\"),\n      divs[5].classList.contains(\"some-class\"),\n      divs[0].textContent,\n      divs[5].textContent,\n    ] as [boolean, boolean, string | null, string | null];\n  });\n  expect(results[0]).toBeTruthy();\n  expect(results[1]).toBeFalsy();\n  expect(results[2]).toBe(\"hello\");\n  expect(results[3]).toBe(\"item5\");\n}\n\ntest.beforeEach(async ({ page }) => {\n  await page.goto(\"about:blank\");\n  await page.addScriptTag({ path: bundlePath });\n  await page.evaluate(() => {\n    window.$ = (window as any).carbonium.$;\n    document.body.innerHTML = \"\";\n    for (let i = 0; i < 6; i++) {\n      const div = document.createElement(\"div\");\n      div.textContent = `item${i}`;\n      document.body.appendChild(div);\n    }\n  });\n});\n\ntest.describe(\"$\", () => {\n  test(\"textContent one element\", async ({ page }) => {\n    const text = await page.evaluate(() => {\n      $(\"div:first-child\").textContent = \"hello\";\n      return document.getElementsByTagName(\"div\")[0].textContent;\n    });\n    expect(text).toBe(\"hello\");\n  });\n\n  test(\"textContent one element with type\", async ({ page }) => {\n    const text = await page.evaluate(() => {\n      const div = $(\"div:first-child\");\n      div.textContent = \"hello\";\n      return document.getElementsByTagName(\"div\")[0].textContent;\n    });\n    expect(text).toBe(\"hello\");\n  });\n\n  test(\"textContent all elements\", async ({ page }) => {\n    const text = await page.evaluate(() => {\n      $(\"div\").textContent = \"hello\";\n      return document.body.textContent;\n    });\n    expect(text).toBe(\"hellohellohellohellohellohello\");\n  });\n\n  test(\"length\", async ({ page }) => {\n    const length = await page.evaluate(() => {\n      return $(\"div\").length;\n    });\n    expect(length).toBe(6);\n  });\n\n  test(\"forEach\", async ({ page }) => {\n    const texts = await page.evaluate(() => {\n      const divs = $(\"div\");\n      divs.forEach((div: HTMLElement, i: number) => {\n        div.textContent = `div ${i}`;\n      });\n      return [divs[0].textContent, divs[5].textContent];\n    });\n    expect(texts[0]).toBe(\"div 0\");\n    expect(texts[1]).toBe(\"div 5\");\n  });\n\n  test(\"for of\", async ({ page }) => {\n    const texts = await page.evaluate(() => {\n      const divs = $(\"div\");\n      let i = 0;\n      for (const div of divs) {\n        (div as HTMLElement).textContent = `div ${i++}`;\n      }\n      return [divs[0].textContent, divs[5].textContent];\n    });\n    expect(texts[0]).toBe(\"div 0\");\n    expect(texts[1]).toBe(\"div 5\");\n  });\n\n  test(\"setAttribute all elements\", async ({ page }) => {\n    const attrs = await page.evaluate(() => {\n      $(\"div\").setAttribute(\"aria-label\", \"List item\");\n      const divs = document.getElementsByTagName(\"div\");\n      return [\n        divs[0].getAttribute(\"aria-label\"),\n        divs[1].getAttribute(\"aria-label\"),\n        divs[5].getAttribute(\"aria-label\"),\n      ];\n    });\n    expect(attrs[0]).toBe(\"List item\");\n    expect(attrs[1]).toBe(\"List item\");\n    expect(attrs[2]).toBe(\"List item\");\n  });\n\n  test(\"filter\", async ({ page }) => {\n    const text = await page.evaluate(() => {\n      $(\"div\").filter(\n        (el: HTMLElement) => el.textContent == \"item1\"\n      ).textContent = \"hello\";\n      return document.body.textContent;\n    });\n    expect(text).toBe(\"item0helloitem2item3item4item5\");\n  });\n\n  test(\"class add method\", async ({ page }) => {\n    const results = await page.evaluate(() => {\n      $(\"div\").classList.add(\"some-class\");\n      const divs = document.getElementsByTagName(\"div\");\n      return [\n        divs[0].classList.contains(\"some-class\"),\n        divs[5].classList.contains(\"some-class\"),\n      ];\n    });\n    expect(results[0]).toBeTruthy();\n    expect(results[1]).toBeTruthy();\n  });\n\n  test(\"rel add and contains method\", async ({ page }) => {\n    const result = await page.evaluate(() => {\n      const a = document.createElement(\"a\");\n      a.relList.add(\"some-class\");\n      return a.relList.contains(\"some-class\");\n    });\n    expect(result).toBeTruthy();\n  });\n\n  test(\"class value property\", async ({ page }) => {\n    const value = await page.evaluate(() => {\n      $(\"div\").classList.add(\"some-class\");\n      const divs = document.getElementsByTagName(\"div\");\n      return divs[0].classList.value;\n    });\n    expect(value).toBe(\"some-class\");\n  });\n\n  test(\"class add method and textContent property\", async ({ page }) => {\n    await page.evaluate(() => {\n      $(\"div:first-child\").classList.add(\"some-class\").textContent = \"hello\";\n    });\n    await checkFirstClassAndText(page);\n  });\n\n  test(\"filter and class add method and textContent property\", async ({\n    page,\n  }) => {\n    await page.evaluate(() => {\n      $(\"div\")\n        .filter((el: HTMLElement) => el.textContent == \"item0\")\n        .classList.add(\"some-class\").textContent = \"hello\";\n    });\n    await checkFirstClassAndText(page);\n  });\n\n  test(\"filter and style setProperty method and textContent property\", async ({\n    page,\n  }) => {\n    const results = await page.evaluate(() => {\n      $(\"div\")\n        .filter((el: HTMLElement) => el.textContent == \"item0\")\n        .style.setProperty(\"--leftmargin\", \"10px\").textContent = \"hello\";\n      const divs = document.getElementsByTagName(\"div\");\n      return [\n        divs[0].style.getPropertyValue(\"--leftmargin\"),\n        divs[5].style.getPropertyValue(\"--leftmargin\"),\n        divs[0].textContent,\n        divs[5].textContent,\n      ];\n    });\n    expect(results[0]).toBe(\"10px\");\n    expect(results[1]).toBe(\"\");\n    expect(results[2]).toBe(\"hello\");\n    expect(results[3]).toBe(\"item5\");\n  });\n\n  test(\"combined\", async ({ page }) => {\n    const results = await page.evaluate(() => {\n      $(\"div\")\n        .forEach(\n          (el: HTMLElement) =>\n            (el.title = `A div with content ${el.textContent}`)\n        )\n        .setAttribute(\"aria-label\", \"List item\")\n        .filter((el: HTMLElement) => el.textContent == \"item1\").textContent =\n        \"hello\";\n      const divs = document.getElementsByTagName(\"div\");\n      return [\n        divs[0].getAttribute(\"aria-label\"),\n        divs[5].getAttribute(\"aria-label\"),\n        divs[0].getAttribute(\"title\"),\n        divs[5].getAttribute(\"title\"),\n        document.body.textContent,\n      ];\n    });\n    expect(results[0]).toBe(\"List item\");\n    expect(results[1]).toBe(\"List item\");\n    expect(results[2]).toBe(\"A div with content item0\");\n    expect(results[3]).toBe(\"A div with content item5\");\n    expect(results[4]).toBe(\"item0helloitem2item3item4item5\");\n  });\n\n  test(\"textContent empty list\", async ({ page }) => {\n    await page.evaluate(() => {\n      $(\"div.non-existent\").textContent = \"hello\";\n    });\n  });\n\n  test(\"setAttribute empty list\", async ({ page }) => {\n    await page.evaluate(() => {\n      $(\"div.non-existent\").setAttribute(\"aria-label\", \"List item\");\n    });\n  });\n\n  test(\"call element specific function\", async ({ page }) => {\n    await page.evaluate(() => {\n      const input = document.createElement(\"input\");\n      document.querySelector(\"div:first-child\")!.appendChild(input);\n      $(\"input\").select();\n    });\n  });\n\n  test(\"addEventListener\", async ({ page }) => {\n    const fired = await page.evaluate(\n      () =>\n        new Promise<boolean>((resolve) => {\n          $(\"div:first-child\").addEventListener(\"click\", () => resolve(true));\n          $(\"div:first-child\").click();\n        })\n    );\n    expect(fired).toBe(true);\n  });\n\n  test(\"canvas\", async ({ page }) => {\n    await page.evaluate(() => {\n      const canvas = document.createElement(\"canvas\");\n      $(\"div:nth-child(1)\").appendChild(canvas);\n      const ctx = $(\"canvas\").getContext(\"2d\", { alpha: false });\n      ctx.fillRect(0, 0, 100, 100);\n    });\n  });\n\n  test(\"style set/get\", async ({ page }) => {\n    const color = await page.evaluate(() => {\n      $(\"div:nth-child(1)\").style.color = \"red\";\n      return $(\"div:nth-child(1)\").style.color;\n    });\n    expect(color).toBe(\"red\");\n  });\n\n  test(\"Parse HTML\", async ({ page }) => {\n    const results = await page.evaluate(() => {\n      const div$ = $(\"<div class='a1'>b1</div>\");\n      const hasClass = div$.classList.contains(\"a1\");\n      $(\"div:first-child\").appendChild(div$[0]);\n      return [hasClass, $(\".a1\").length, $(\".a1\").textContent];\n    });\n    expect(results[0]).toBeTruthy();\n    expect(results[1]).toBe(1);\n    expect(results[2]).toBe(\"b1\");\n  });\n\n  test(\"Set\", async ({ page }) => {\n    const result = await page.evaluate(() => {\n      const set = new Set([\"1a\", \"2a\", \"3a\"]);\n      let result = \"\";\n      set.forEach((item) => {\n        result += `[${item}]`;\n      });\n      return result;\n    });\n    expect(result).toBe(\"[1a][2a][3a]\");\n  });\n\n  test(\"Custom Element\", async ({ page }) => {\n    await page.evaluate(() => {\n      class GolInfo extends HTMLElement {\n        connectedCallback() {\n          $(\"nnn\").addEventListener(\"click\", () => {\n            console.log(\"click\");\n          });\n        }\n      }\n      customElements.define(\"gol-info\", GolInfo);\n      const i = document.createElement(\"gol-info\");\n      document.body.appendChild(i);\n    });\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"rootDir\": \"./src\",\n    \"moduleResolution\": \"bundler\",\n    \"sourceMap\": true,\n    \"declaration\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  }
]