[
  {
    "path": ".circleci/config.yml",
    "content": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more details\n#\nversion: 2\njobs:\n  build:\n    docker:\n      # specify the version you desire here\n      - image: cimg/node:16.13.2\n\n      # Specify service dependencies here if necessary\n      # CircleCI maintains a library of pre-built images\n      # documented at https://circleci.com/docs/2.0/circleci-images/\n      # - image: circleci/mongo:3.4.4\n\n    working_directory: ~/repo\n\n    steps:\n      - checkout\n\n      # Download and cache dependencies\n      - restore_cache:\n          keys:\n            - v1-dependencies-{{ checksum \"package.json\" }}\n            # fallback to using the latest cache if no exact match is found\n            - v1-dependencies-\n\n      - run: npm install\n\n      - run: npm run build\n\n      - persist_to_workspace:\n          root: ~/repo\n          paths:\n            - dist\n\n      - save_cache:\n          paths:\n            - node_modules\n          key: v1-dependencies-{{ checksum \"package.json\" }}\n\n  test:\n    docker:\n      - image: cimg/node:16.13.2\n    working_directory: ~/repo\n    steps:\n      - checkout\n\n      # Download and cache dependencies\n      - restore_cache:\n          keys:\n            - v1-dependencies-{{ checksum \"package.json\" }}\n            # fallback to using the latest cache if no exact match is found\n            - v1-dependencies-\n\n      - run: npm install\n      - run:\n          name: Test\n          command: npm run test\n      - run:\n          name: Lint\n          command: npm run lint\n\n      # run tests!\n      #- run: yarn test\n  deploy:\n    docker:\n      - image: circleci/python:2.7-jessie\n    working_directory: ~/repo\n    steps:\n      - attach_workspace:\n          # Must be absolute path or relative path from working_directory\n          at: ~/repo\n      - run:\n          name: Install awscli\n          command: sudo pip install awscli\n      - run:\n          name: Deploy to S3\n          command: aws s3 sync --acl public-read ~/repo/dist s3://wds-1.com/ --delete\n\nworkflows:\n  version: 2\n  build-deploy:\n    jobs:\n      - build\n      - test:\n          requires:\n            - build\n      - deploy:\n          requires:\n            - test\n          filters:\n            branches:\n              only: master\n"
  },
  {
    "path": ".eslintignore",
    "content": "src/assets/**/*.js"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": [\"eslint:recommended\", \"plugin:react/recommended\"],\n  \"env\": {\n    \"browser\": true,\n    \"jest\": true,\n    \"es6\": true\n  },\n  \"rules\": {\n    \"import/prefer-default-export\": 0,\n    \"global-require\": 0,\n    \"jsx-a11y/label-has-for\": [0],\n    \"jsx-a11y/label-has-associated-control\": [0],\n    \"default-param-last\": 0,\n    \"implicit-arrow-linebreak\": 0\n  },\n  \"parser\": \"@babel/eslint-parser\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n.vscode\n.DS_Store\ndist/*"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true\n}"
  },
  {
    "path": "CNAME",
    "content": "wds-1.com"
  },
  {
    "path": "README.md",
    "content": "# Web Drum Sequencer\n\nA browser-based drum machine and sequencer built with the Web Audio API, React, and Redux.\n\n## Demo\n\nhttps://wds-1.com\n\n## Features\n * Swap drum samples\n * Choose drum samples from file\n * Pattern selector to save up to 8 patterns per drum kit\n * BPM and swing control\n * Sample hit buttons\n * Gain and pan\n * Reverb\n * Mute and solo\n * Pitch shift\n * Preset system for saving and loading drum kits\n * Works offline with service worker and caching\n * Installable as PWA\n * Drag to reorder channels\n\n## Circle CI status\n\n[![CircleCI](https://circleci.com/gh/stufreen/web-drum-sequencer.svg?style=svg)](https://circleci.com/gh/stufreen/web-drum-sequencer)\n\n## Installation\n\nTo run a local development server:\n```\nnpm install\nnpm run start\n```\n\nTo build a production version: `npm run build`\n\n## Tests\n\n```\nnpm run test\n```\n\n## Thank You\n * [React-Select](https://github.com/JedWatson/react-select)\n * [Webaudio-Controls](https://github.com/g200kg/webaudio-controls)\n * Chris Wilson's article [here](https://www.html5rocks.com/en/tutorials/audio/scheduling/)\n * [Voxengo impluse response](https://www.voxengo.com/impulses/)\n * [Jost* typeface](https://github.com/indestructible-type/Jost)\n * [Draggable](https://shopify.github.io/draggable/)\n"
  },
  {
    "path": "__mocks__/fileMock.js",
    "content": "module.exports = 'test-file-stub';\n"
  },
  {
    "path": "__mocks__/styleMock.js",
    "content": "module.exports = {};\n"
  },
  {
    "path": "babel.config.js",
    "content": "const presets = [\n  [\n    \"@babel/env\",\n    {\n      targets: \"> 0.25%, not dead\",\n      useBuiltIns: \"usage\",\n    },\n  ],\n  \"@babel/preset-react\",\n];\n\nmodule.exports = { presets };"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <!-- Global site tag (gtag.js) - Google Analytics -->\n    <script\n      async\n      src=\"https://www.googletagmanager.com/gtag/js?id=UA-66781633-3\"\n    ></script>\n    <script>\n      window.dataLayer = window.dataLayer || [];\n      function gtag() {\n        dataLayer.push(arguments);\n      }\n      gtag('js', new Date());\n      gtag('config', 'UA-66781633-3');\n    </script>\n    <meta charset=\"UTF-8\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\n    />\n    <meta\n      name=\"Description\"\n      content=\"Browser-based drum machine and sequencer for crafting beats on the go. You can save your creations and work offline.\"\n    />\n    <title>WDS-1: Web Drum Sequencer</title>\n    <link rel=\"manifest\" href=\"manifest.json\" crossorigin=\"use-credentials\" />\n    <link rel=\"shortcut icon\" href=\"./assets/icons/favicon.ico\" />\n    <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <meta name=\"application-name\" content=\"web-drum-sequencer\" />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"57x57\"\n      href=\"assets/icons/apple-icon-57x57.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"60x60\"\n      href=\"assets/icons/apple-icon-60x60.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"72x72\"\n      href=\"assets/icons/apple-icon-72x72.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"76x76\"\n      href=\"assets/icons/apple-icon-76x76.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"114x114\"\n      href=\"assets/icons/apple-icon-114x114.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"120x120\"\n      href=\"assets/icons/apple-icon-120x120.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"144x144\"\n      href=\"assets/icons/apple-icon-144x144.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"152x152\"\n      href=\"assets/icons/apple-icon-152x152.png\"\n    />\n    <link\n      rel=\"apple-touch-icon\"\n      sizes=\"180x180\"\n      href=\"assets/icons/apple-icon-180x180.png\"\n    />\n    <link\n      rel=\"icon\"\n      type=\"image/png\"\n      sizes=\"192x192\"\n      href=\"assets/icons/android-icon-192x192.png\"\n    />\n    <link\n      rel=\"icon\"\n      type=\"image/png\"\n      sizes=\"32x32\"\n      href=\"assets/icons/favicon-32x32.png\"\n    />\n    <link\n      rel=\"icon\"\n      type=\"image/png\"\n      sizes=\"96x96\"\n      href=\"assets/icons/favicon-96x96.png\"\n    />\n    <link\n      rel=\"icon\"\n      type=\"image/png\"\n      sizes=\"16x16\"\n      href=\"assets/icons/favicon-16x16.png\"\n    />\n    <meta name=\"msapplication-TileColor\" content=\"#202429\" />\n    <meta\n      name=\"msapplication-TileImage\"\n      content=\"assets/icons/mstile-144x144.png\"\n    />\n    <meta\n      name=\"msapplication-config\"\n      content=\"assets/icons/browserconfig.xml\"\n    />\n    <meta\n      property=\"og:url\"\n      content=\"https://stufreen.github.io/web-drum-sequencer\"\n    />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:title\" content=\"WDS-1: Web Drum Sequencer\" />\n    <meta\n      property=\"og:image\"\n      content=\"https://stufreen.github.io/web-drum-sequencer/wds-1-screen.png\"\n    />\n    <meta\n      property=\"og:description\"\n      content=\"Browser-based drum machine and sequencer for crafting beats on the go. You can save your creations and work offline.\"\n    />\n    <meta property=\"og:locale\" content=\"en_US\" />\n    <meta property=\"og:article:author\" content=\"Stu Freen\" />\n    <meta name=\"twitter:card\" content=\"summary\" />\n    <meta name=\"twitter:creator\" content=\"@stafree\" />\n    <meta\n      name=\"twitter:url\"\n      content=\"https://stufreen.github.io/web-drum-sequencer\"\n    />\n    <meta name=\"twitter:title\" content=\"WDS-1: Web Drum Sequencer\" />\n    <meta\n      name=\"twitter:description\"\n      content=\"Browser-based drum machine and sequencer for crafting beats on the go. You can save your creations and work offline.\"\n    />\n    <meta\n      name=\"twitter:image\"\n      content=\"https://stufreen.github.io/web-drum-sequencer/wds-1-screen.png\"\n    />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <noscript> You need to enable JavaScript to run this app. </noscript>\n    <script type=\"module\" src=\"src/index.jsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "manifest.json",
    "content": "{\n  \"icons\": [\n    {\n      \"src\": \"/assets/icons/icon-48x48.png\",\n      \"sizes\": \"48x48\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-72x72.png\",\n      \"sizes\": \"72x72\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-96x96.png\",\n      \"sizes\": \"96x96\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-128x128.png\",\n      \"sizes\": \"128x128\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-144x144.png\",\n      \"sizes\": \"144x144\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-152x152.png\",\n      \"sizes\": \"152x152\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-384x384.png\",\n      \"sizes\": \"384x384\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    },\n    {\n      \"src\": \"/assets/icons/icon-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\",\n      \"purpose\": \"maskable any\"\n    }\n  ],\n  \"name\": \"WDS-1: Wed Drum Sequencer\",\n  \"short_name\": \"WDS-1\",\n  \"orientation\": \"portrait\",\n  \"display\": \"standalone\",\n  \"start_url\": \"/\",\n  \"background_color\": \"#202429\",\n  \"theme_color\": \"#000000\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"web-drum-sequencer\",\n  \"version\": \"0.2.4\",\n  \"description\": \"A drum machine and sequencer built with the Web Audio API and React.\",\n  \"main\": \"dist/index.html\",\n  \"scripts\": {\n    \"start\": \"vite --host\",\n    \"build\": \"rm -rf dist && vite build\",\n    \"lint\": \"eslint src\",\n    \"test\": \"jest\",\n    \"gh-pages\": \"git subtree push --prefix dist origin gh-pages\"\n  },\n  \"author\": \"Stu Freen (http://www.stufreen.com)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/stufreen/web-drum-sequencer.git\"\n  },\n  \"keywords\": [\n    \"Web Audio\",\n    \"Web Audio API\",\n    \"Drum Machine\",\n    \"Playback\",\n    \"Effect\",\n    \"Instrument\",\n    \"React\",\n    \"Redux\",\n    \"Interactive Music\"\n  ],\n  \"dependencies\": {\n    \"@shopify/draggable\": \"^1.0.0-beta.8\",\n    \"animol\": \"1.0.9\",\n    \"prop-types\": \"^15.6.2\",\n    \"ramda\": \"^0.25.0\",\n    \"react\": \"^16.4.1\",\n    \"react-dom\": \"^16.4.1\",\n    \"react-redux\": \"^5.0.7\",\n    \"react-select\": \"^2.0.0\",\n    \"recompose\": \"^0.27.1\",\n    \"redux\": \"^4.0.0\",\n    \"redux-persist\": \"^5.10.0\",\n    \"redux-thunk\": \"^2.3.0\",\n    \"reselect\": \"^3.0.1\",\n    \"serviceworker-webpack-plugin\": \"^1.0.1\",\n    \"styled-components\": \"^3.3.3\",\n    \"styled-system\": \"^3.0.2\",\n    \"uuid\": \"^3.3.2\"\n  },\n  \"jest\": {\n    \"testURL\": \"http://localhost\",\n    \"moduleNameMapper\": {\n      \"\\\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$\": \"<rootDir>/__mocks__/fileMock.js\",\n      \"\\\\.(css|less)$\": \"<rootDir>/__mocks__/styleMock.js\"\n    }\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.22.9\",\n    \"@babel/eslint-parser\": \"^7.22.9\",\n    \"@babel/preset-env\": \"^7.22.9\",\n    \"@babel/preset-react\": \"^7.22.5\",\n    \"@vitejs/plugin-react-refresh\": \"^1.3.6\",\n    \"eslint\": \"^8.46.0\",\n    \"eslint-config-airbnb\": \"^19.0.4\",\n    \"eslint-plugin-import\": \"^2.27.5\",\n    \"eslint-plugin-jsx-a11y\": \"^6.7.1\",\n    \"eslint-plugin-react\": \"^7.33.1\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"jest\": \"^29.6.2\",\n    \"vite\": \"^4.4.7\"\n  }\n}\n"
  },
  {
    "path": "public/assets/icons/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig><msapplication><tile><square70x70logo src=\"/ms-icon-70x70.png\"/><square150x150logo src=\"/ms-icon-150x150.png\"/><square310x310logo src=\"/ms-icon-310x310.png\"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>"
  },
  {
    "path": "public/sw.js",
    "content": "/* eslint no-restricted-globals: 1 */\n\nconst addToCache = (event, fetchResponse) => {\n  // Check if we received a valid response\n  if (!fetchResponse || fetchResponse.status !== 200 || fetchResponse.type !== 'basic') {\n    return fetchResponse;\n  }\n\n  // IMPORTANT: Clone the response. A response is a stream\n  // and because we want the browser to consume the response\n  // as well as the cache consuming the response, we need\n  // to clone it so we have two streams.\n  const responseToCache = fetchResponse.clone();\n\n  caches.open('wdsCache').then((cache) => {\n    cache.put(event.request, responseToCache);\n  });\n\n  return fetchResponse;\n};\n\nconst fetchAndCache = (event, fetchRequest) => fetch(fetchRequest)\n  .then(fetchResponse => addToCache(event, fetchResponse));\n\nself.addEventListener('fetch', (event) => {\n  // IMPORTANT: Clone the request. A request is a stream and\n  // can only be consumed once. Since we are consuming this\n  // once by cache and once by the browser for fetch, we need\n  // to clone the response.\n  const fetchRequest = event.request.clone();\n\n  // Try to get files from the \"assets\" directory from cache first\n  if (fetchRequest.url.indexOf('/assets/') >= 0) {\n    event.respondWith(\n      caches.match(fetchRequest)\n        .then(response => response || fetchAndCache(event, fetchRequest)),\n    );\n  } else {\n    event.respondWith(\n      fetchAndCache(event, fetchRequest),\n    );\n  }\n});\n"
  },
  {
    "path": "src/__mocks__/samples.config.js",
    "content": "export default [\n  {\n    name: 'Fake sample A',\n    url: '/fake/sample/a/url.wav',\n  },\n  {\n    name: 'Fake sample B',\n    url: '/fake/sample/b/url.wav',\n  },\n];\n"
  },
  {
    "path": "src/assets/js/webaudio-controls.js",
    "content": "/* *\n *\n *  WebAudio-Controls is based on\n *    webaudio-knob by Eiji Kitamura http://google.com/+agektmr\n *    webaudio-slider by RYoya Kawai https://plus.google.com/108242669191458983485/posts\n *    webaudio-switch by Keisuke Ai http://d.hatena.ne.jp/aike/\n *  Integrated and enhanced by g200kg http://www.g200kg.com/\n *\n *\tCopyright 2013 Eiji Kitamura / Ryoya KAWAI / Keisuke Ai / g200kg(Tatsuya Shinyagaito)\n *\n *\t Licensed under the Apache License, Version 2.0 (the \"License\");\n *\t you may not use this file except in compliance with the License.\n *\t You may obtain a copy of the License at\n *\n *\t http://www.apache.org/licenses/LICENSE-2.0\n *\n *\t Unless required by applicable law or agreed to in writing, software\n *\t distributed under the License is distributed on an \"AS IS\" BASIS,\n *\t WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *\t See the License for the specific language governing permissions and\n *\t limitations under the License.\n *\n * */\nif(window.customElements){\n  let styles=document.createElement(\"style\");\n  styles.innerHTML=\n`#webaudioctrl-context-menu {\n  display: none;\n  position: absolute;\n  z-index: 10;\n  padding: 0;\n  width: 100px;\n  color:#eee;\n  background-color: #268;\n  border: solid 1px #888;\n  box-shadow: 1px 1px 2px #888;\n  font-family: sans-serif;\n  font-size: 11px;\n  line-height:1.7em;\n  text-align:center;\n  cursor:pointer;\n  color:#fff;\n  list-style: none;\n}\n#webaudioctrl-context-menu.active {\n  display: block;\n}\n.webaudioctrl-context-menu__item {\n  display: block;\n  margin: 0;\n  padding: 0;\n  color: #000;\n  background-color:#eee;\n  text-decoration: none;\n}\n.webaudioctrl-context-menu__title{\n  font-weight:bold;\n}\n.webaudioctrl-context-menu__item:last-child {\n  margin-bottom: 0;\n}\n.webaudioctrl-context-menu__item:hover {\n  background-color: #b8b8b8;\n}\n`;\n  document.head.appendChild(styles);\n  let midimenu=document.createElement(\"ul\");\n  midimenu.id=\"webaudioctrl-context-menu\";\n  midimenu.innerHTML=\n`<li class=\"webaudioctrl-context-menu__title\">MIDI Learn</li>\n<li class=\"webaudioctrl-context-menu__item\" id=\"webaudioctrl-context-menu-learn\" onclick=\"webAudioControlsMidiManager.contextMenuLearn()\">Learn</li>\n<li class=\"webaudioctrl-context-menu__item\" onclick=\"webAudioControlsMidiManager.contextMenuClear()\">Clear</li>\n<li class=\"webaudioctrl-context-menu__item\" onclick=\"webAudioControlsMidiManager.contextMenuClose()\">Close</li>\n`;\n  let opt={\n    useMidi:0,\n    midilearn:0,\n    mididump:0,\n    outline:0,\n    knobSrc:null,\n    knobSprites:0,\n    knobWidth:0,\n    knobHeight:0,\n    knobDiameter:64,\n    knobColors:\"#e00;#000;#000\",\n    sliderSrc:null,\n    sliderKnobsrc:null,\n    sliderWidth:0,\n    sliderHeight:0,\n    sliderKnobwidth:0,\n    sliderKnobheight:0,\n    sliderDitchlength:0,\n    sliderColors:\"#e00;#000;#fcc\",\n    switchWidth:0,\n    switchHeight:0,\n    switchDiameter:24,\n    switchColors:\"#e00;#000;#fcc\",\n    paramWidth:32,\n    paramHeight:16,\n    paramColors:\"#fff;#000\",\n    xypadColors:\"#e00;#000;#fcc\",\n  };\n  if(window.WebAudioControlsOptions)\n    Object.assign(opt,window.WebAudioControlsOptions);\n  class WebAudioControlsWidget extends HTMLElement{\n    constructor(){\n      super();\n      this.addEventListener(\"keydown\",this.keydown);\n      this.addEventListener(\"mousedown\",this.pointerdown,{passive:false});\n      this.addEventListener(\"touchstart\",this.pointerdown,{passive:false});\n      this.addEventListener(\"wheel\",this.wheel,{passive:false});\n      this.addEventListener(\"mouseover\",this.pointerover);\n      this.addEventListener(\"mouseout\",this.pointerout);\n      this.addEventListener(\"contextmenu\",this.contextMenu);\n      this.hover=this.drag=0;\n      document.body.appendChild(midimenu);\n      this.basestyle=`\n.webaudioctrl-tooltip{\n  display:inline-block;\n  position:absolute;\n  margin:0 -1000px;\n  z-index: 999;\n  background:#eee;\n  color:#000;\n  border:1px solid #666;\n  border-radius:4px;\n  padding:5px 10px;\n  text-align:center;\n  left:0; top:0;\n  font-size:11px;\n  opacity:0;\n  visibility:hidden;\n}\n.webaudioctrl-tooltip:before{\n  content: \"\";\n  position: absolute;\n  top: 100%;\n  left: 50%;\n  margin-left: -8px;\n  border: 8px solid transparent;\n  border-top: 8px solid #666;\n}\n.webaudioctrl-tooltip:after{\n  content: \"\";\n  position: absolute;\n  top: 100%;\n  left: 50%;\n  margin-left: -6px;\n  border: 6px solid transparent;\n  border-top: 6px solid #eee;\n}\n`;\n    }\n    sendEvent(ev){\n      let event;\n      event=document.createEvent(\"HTMLEvents\");\n      event.initEvent(ev,false,true);\n      this.dispatchEvent(event);\n    }\n    getAttr(n,def){\n      let v=this.getAttribute(n);\n      if(v==\"\"||v==null) return def;\n      switch(typeof(def)){\n      case \"number\":\n        if(v==\"true\") return 1;\n        v=+v;\n        if(isNaN(v)) return 0;\n        return v;\n      }\n      return v;\n    }\n    showtip(d){\n      function valstr(x,c,type){\n        switch(type){\n        case \"x\": return (x|0).toString(16);\n        case \"X\": return (x|0).toString(16).toUpperCase();\n        case \"d\": return (x|0).toString();\n        case \"f\": return x.toFixed(c);\n        case \"s\": return x.toString();\n        }\n        return \"\";\n      }\n      function numformat(s,x){\n        if(typeof(x)==\"undefined\")\n          return;\n        let i=s.indexOf(\"%\");\n        let c=[0,0],type=0,m=0,r=\"\",j=i+1;\n        for(;j<s.length;++j){\n          if(\"dfxXs\".indexOf(s[j])>=0){\n            type=s[j];\n            break;\n          }\n          if(s[j]==\".\")\n            m=1;\n          else\n            c[m]=c[m]*10+parseInt(s[j]);\n        }\n        if(typeof(x)==\"number\")\n          r=valstr(x,c[1],type);\n        else\n          r=valstr(x.x,c[1],type)+\",\"+valstr(x.y,c[1],type);\n        if(c[0]>0)\n          r=(\"               \"+r).slice(-c[0]);\n        r=s.replace(/%.*[xXdfs]/,r);\n        return r;\n      }\n      let s=this.tooltip;\n      if(this.drag||this.hover){\n        if(this.valuetip){\n          if(s==null)\n            s=`%.${this.digits}f`;\n          else if(s.indexOf(\"%\")<0)\n            s+=` : %.${this.digits}f`;\n        }\n        if(s){\n          this.ttframe.innerHTML=numformat(s,this.convValue);\n          this.ttframe.style.display=\"inline-block\";\n          this.ttframe.style.width=\"auto\";\n          this.ttframe.style.height=\"auto\";\n          this.ttframe.style.transition=\"opacity 0.5s \"+d+\"s,visibility 0.5s \"+d+\"s\";\n          this.ttframe.style.opacity=0.9;\n          this.ttframe.style.visibility=\"visible\";\n          let rc=this.getBoundingClientRect(),rc2=this.ttframe.getBoundingClientRect(),rc3=document.documentElement.getBoundingClientRect();\n          this.ttframe.style.left=((rc.width-rc2.width)*0.5+1000)+\"px\";\n          this.ttframe.style.top=(-rc2.height-8)+\"px\";\n          return;\n        }\n      }\n      this.ttframe.style.transition=\"opacity 0.1s \"+d+\"s,visibility 0.1s \"+d+\"s\";\n      this.ttframe.style.opacity=0;\n      this.ttframe.style.visibility=\"hidden\";\n    }\n    pointerover(e) {\n      this.hover=1;\n      this.showtip(0.6);\n    }\n    pointerout(e) {\n      this.hover=0;\n      this.showtip(0);\n    }\n    contextMenu(e){\n      if(window.webAudioControlsMidiManager && this.midilearn)\n        webAudioControlsMidiManager.contextMenuOpen(e,this);\n      e.preventDefault();\n      e.stopPropagation();\n    }\n    setMidiController(channel, cc) {\n      if (this.listeningToThisMidiController(channel, cc)) return;\n      this.midiController={ 'channel': channel, 'cc': cc};\n      console.log(\"Added mapping for channel=\" + channel + \" cc=\" + cc + \" tooltip=\" + this.tooltip);\n    }\n    listeningToThisMidiController(channel, cc) {\n      const c = this.midiController;\n      if((c.channel === channel || c.channel < 0) && c.cc === cc)\n        return true;\n      return false;\n    }\n    processMidiEvent(event){\n      const channel = event.data[0] & 0xf;\n      const controlNumber = event.data[1];\n      if(this.midiMode == 'learn') {\n        this.setMidiController(channel, controlNumber);\n        webAudioControlsMidiManager.contextMenuClose();\n        this.midiMode = 'normal';\n      }\n      if(this.listeningToThisMidiController(channel, controlNumber)) {\n        if(this.tagName==\"WEBAUDIO-SWITCH\"){\n          switch(this.type){\n          case \"toggle\":\n            if(event.data[2]>=64)\n              this.setValue(1-this.value,true);\n            break;\n          case \"kick\":\n            this.setValue(event.data[2]>=64?1:0);\n            break;\n          case \"radio\":\n            let els=document.querySelectorAll(\"webaudio-switch[type='radio'][group='\"+this.group+\"']\");\n            for(let i=0;i<els.length;++i){\n              if(els[i]==this)\n                els[i].setValue(1);\n              else\n                els[i].setValue(0);\n            }\n            break;\n          }\n        }\n        else{\n          const val = this.min+(this.max-this.min)*event.data[2]/127;\n          this.setValue(val, true);\n        }\n      }\n    }\n  }\n\ntry{\n    customElements.define(\"webaudio-knob\", class WebAudioKnob extends WebAudioControlsWidget {\n    constructor(){\n      super();\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-knob{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  cursor:pointer;\n  font-family: sans-serif;\n  font-size: 11px;\n}\n.webaudio-knob-body{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  vertical-align:bottom;\n}\n</style>\n<div class='webaudio-knob-body' tabindex='1' touch-action='none'></div><div class='webaudioctrl-tooltip'></div>\n`;\n      this.elem=root.childNodes[2];\n      this.ttframe=root.childNodes[3];\n      this.enable=this.getAttr(\"enable\",1);\n      this._src=this.getAttr(\"src\",opt.knobSrc); Object.defineProperty(this,\"src\",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}});\n      this._value=this.getAttr(\"value\",0); Object.defineProperty(this,\"value\",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}});\n      this.defvalue=this.getAttr(\"defvalue\",0);\n      this._min=this.getAttr(\"min\",0); Object.defineProperty(this,\"min\",{get:()=>{return this._min},set:(v)=>{this._min=+v;this.redraw()}});\n      this._max=this.getAttr(\"max\",100); Object.defineProperty(this,\"max\",{get:()=>{return this._max},set:(v)=>{this._max=+v;this.redraw()}});\n      this._step=this.getAttr(\"step\",1); Object.defineProperty(this,\"step\",{get:()=>{return this._step},set:(v)=>{this._step=+v;this.redraw()}});\n      this._sprites=this.getAttr(\"sprites\",opt.knobSprites); Object.defineProperty(this,\"sprites\",{get:()=>{return this._sprites},set:(v)=>{this._sprites=v;this.setupImage()}});\n      this._width=this.getAttr(\"width\",opt.knobWidth); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",opt.knobHeight); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      this._diameter=this.getAttr(\"diameter\",opt.knobDiameter); Object.defineProperty(this,\"diameter\",{get:()=>{return this._diameter},set:(v)=>{this._diameter=v;this.setupImage()}});\n      this._colors=this.getAttr(\"colors\",opt.knobColors); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.sensitivity=this.getAttr(\"sensitivity\",1);\n      this.valuetip=this.getAttr(\"valuetip\",1);\n      this.tooltip=this.getAttr(\"tooltip\",null);\n      this.conv=this.getAttr(\"conv\",null);\n      if(this.conv)\n        this.convValue=eval(this.conv)(this._value);\n      else\n        this.convValue=this._value;\n      this.midilearn=this.getAttr(\"midilearn\",opt.midilearn);\n      this.midicc=this.getAttr(\"midicc\",null);\n\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n          let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n          let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n          this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      this.digits=0;\n      this.coltab=[\"#e00\",\"#000\",\"#000\"];\n      if(window.webAudioControlsMidiManager)\n//        window.webAudioControlsMidiManager.updateWidgets();\n        window.webAudioControlsMidiManager.addWidget(this);\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      this.kw=this.width||this.diameter;\n      this.kh=this.height||this.diameter;\n      if(!this.src){\n        if(this.colors)\n          this.coltab = this.colors.split(\";\");\n        if(!this.coltab)\n          this.coltab=[\"#e00\",\"#000\",\"#000\"];\n        let svg=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"64\" height=\"6464\" preserveAspectRatio=\"none\">\n<radialGradient id=\"gr\" cx=\"30%\" cy=\"30%\"><stop offset=\"0%\" stop-color=\"${this.coltab[2]}\"/><stop offset=\"100%\" stop-color=\"${this.coltab[1]}\"/></radialGradient>\n<defs><circle id=\"B\" cx=\"32\" cy=\"32\" r=\"30\" fill=\"url(#gr)\"/></defs>\n<defs><line id=\"K\" x1=\"32\" y1=\"28\" x2=\"32\" y2=\"7\" stroke-linecap=\"round\" stroke-width=\"6\" stroke=\"${this.coltab[0]}\"/></defs>`;\n        for(let i=0;i<101;++i){\n          svg += `<use xlink:href=\"#B\" y=\"${64*i}\"/>`;\n          svg += `<use xlink:href=\"#K\" y=\"${64*i}\" transform=\"rotate(${(-135+270*i/101).toFixed(2)},32,${64*i+32})\"/>`;\n        }\n        svg += \"</svg>\";\n        this.elem.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svg)+\")\";\n//        this.elem.style.backgroundSize = \"100% 10100%\";\n        this.elem.style.backgroundSize = `${this.kw}px ${this.kh*101}px`;\n      }\n      else{\n        this.elem.style.backgroundImage = \"url(\"+(this.src)+\")\";\n        if(!this.sprites)\n          this.elem.style.backgroundSize = \"100% 100%\";\n        else{\n//          this.elem.style.backgroundSize = `100% ${(this.sprites+1)*100}%`;\n          this.elem.style.backgroundSize = `${this.kw}px ${this.kh*(this.sprites+1)}px`;\n        }\n      }\n      this.elem.style.outline=this.outline?\"\":\"none\";\n      this.elem.style.width=this.kw+\"px\";\n      this.elem.style.height=this.kh+\"px\";\n      this.style.height=this.kh+\"px\";\n      this.redraw();\n    }\n    redraw() {\n      this.digits=0;\n      if(this.step && this.step < 1) {\n        for(let n = this.step ; n < 1; n *= 10)\n          ++this.digits;\n      }\n      if(this.value<this.min){\n        this.value=this.min;\n        return;\n      }\n      if(this.value>this.max){\n        this.value=this.max;\n        return;\n      }\n      let range = this.max - this.min;\n      let style = this.elem.style;\n      let sp = this.src?this.sprites:100;\n      if(sp>=1){\n        let offset = ((sp * (this.value - this.min) / range) | 0);\n        style.backgroundPosition = \"0px \" + (-offset*this.kh) + \"px\";\n        style.transform = 'rotate(0deg)';\n      } else {\n        let deg = 270 * ((this.value - this.min) / range - 0.5);\n        style.backgroundPosition=\"0px 0px\";\n        style.transform = 'rotate(' + deg + 'deg)';\n      }\n    }\n    _setValue(v){\n      if(this.step)\n        v=(Math.round((v-this.min)/this.step))*this.step+this.min;\n      this._value=Math.min(this.max,Math.max(this.min,v));\n      if(this._value!=this.oldvalue){\n        this.oldvalue=this._value;\n        if(this.conv)\n          this.convValue=eval(this.conv)(this._value);\n        else\n          this.convValue=this._value;\n        this.redraw();\n        this.showtip(0);\n        return 1;\n      }\n      return 0;\n    }\n    setValue(v,f){\n      if(this._setValue(v) && f)\n        this.sendEvent(\"input\"),this.sendEvent(\"change\");\n    }\n    wheel(e) {\n      if (!this.enable)\n        return;\n      let delta=(this.max-this.min)*0.01;\n      delta=e.deltaY>0?-delta:delta;\n      if(!e.shiftKey)\n        delta*=5;\n      if(Math.abs(delta) < this.step)\n        delta = (delta > 0) ? +this.step : -this.step;\n      this.setValue(+this.value+delta,true);\n      e.preventDefault();\n      e.stopPropagation();\n    }\n    pointerdown(ev){\n      if(!this.enable)\n        return;\n      let e=ev;\n      if(ev.touches){\n        e = ev.changedTouches[0];\n        this.identifier=e.identifier;\n      }\n      else {\n        if(e.buttons!=1 && e.button!=0)\n          return;\n      }\n      this.elem.focus();\n      this.drag=1;\n      this.showtip(0);\n      let pointermove=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;i<ev.touches.length;++i){\n            if(ev.touches[i].identifier==this.identifier){\n              e = ev.touches[i];\n              break;\n            }\n          }\n        }\n        if(this.lastShift !== e.shiftKey) {\n          this.lastShift = e.shiftKey;\n          this.startPosX = e.pageX;\n          this.startPosY = e.pageY;\n          this.startVal = this.value;\n        }\n        let offset = (this.startPosY - e.pageY - this.startPosX + e.pageX) * this.sensitivity;\n        this._setValue(this.min + ((((this.startVal + (this.max - this.min) * offset / ((e.shiftKey ? 4 : 1) * 128)) - this.min) / this.step) | 0) * this.step);\n        this.sendEvent(\"input\");\n        if(e.preventDefault)\n          e.preventDefault();\n        if(e.stopPropagation)\n          e.stopPropagation();\n        return false;\n      }\n      let pointerup=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;;){\n            if(ev.changedTouches[i].identifier==this.identifier){\n              break;\n            }\n            if(++i>=ev.changedTouches.length)\n              return;\n          }\n        }\n        this.drag=0;\n        this.showtip(0);\n        this.startPosX = this.startPosY = null;\n        window.removeEventListener('mousemove', pointermove);\n        window.removeEventListener('touchmove', pointermove, {passive:false});\n        window.removeEventListener('mouseup', pointerup);\n        window.removeEventListener('touchend', pointerup);\n        window.removeEventListener('touchcancel', pointerup);\n        document.body.removeEventListener('touchstart', preventScroll,{passive:false});\n        this.sendEvent(\"change\");\n      }\n      let preventScroll=(e)=>{\n        e.preventDefault();\n      }\n      if(e.ctrlKey || e.metaKey)\n        this.setValue(this.defvalue,true);\n      else {\n        this.startPosX = e.pageX;\n        this.startPosY = e.pageY;\n        this.startVal = this.value;\n        window.addEventListener('mousemove', pointermove);\n        window.addEventListener('touchmove', pointermove, {passive:false});\n      }\n      window.addEventListener('mouseup', pointerup);\n      window.addEventListener('touchend', pointerup);\n      window.addEventListener('touchcancel', pointerup);\n      document.body.addEventListener('touchstart', preventScroll,{passive:false});\n      ev.preventDefault();\n      ev.stopPropagation();\n      return false;\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-knob already defined\");\n}\n\ntry{\n  customElements.define(\"webaudio-slider\", class WebAudioSlider extends WebAudioControlsWidget {\n    constructor(){\n      super();\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-slider{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  font-family: sans-serif;\n  font-size: 11px;\n  cursor:pointer;\n}\n.webaudio-slider-body{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  vertical-align:bottom;\n}\n.webaudio-slider-knob{\n  display:inline-block;\n  position:absolute;\n  margin:0;\n  padding:0;\n}\n</style>\n<div class='webaudio-slider-body' tabindex='1' touch-action='none'><div class='webaudio-slider-knob' touch-action='none'></div></div><div class='webaudioctrl-tooltip'></div>\n`;\n      this.elem=root.childNodes[2];\n      this.knob=this.elem.childNodes[0];\n      this.ttframe=root.childNodes[3];\n      this.enable=this.getAttr(\"enable\",1);\n      this._src=this.getAttr(\"src\",opt.sliderSrc); Object.defineProperty(this,\"src\",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}});\n      this._knobsrc=this.getAttr(\"knobsrc\",opt.sliderKnobsrc); Object.defineProperty(this,\"knobsrc\",{get:()=>{return this._knobsrc},set:(v)=>{this._knobsrc=v;this.setupImage()}});\n      this._value=this.getAttr(\"value\",0); Object.defineProperty(this,\"value\",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}});\n      this.defvalue=this.getAttr(\"defvalue\",0);\n      this._min=this.getAttr(\"min\",0); Object.defineProperty(this,\"min\",{get:()=>{return this._min},set:(v)=>{this._min=v;this.redraw()}});\n      this._max=this.getAttr(\"max\",100); Object.defineProperty(this,\"max\",{get:()=>{return this._max},set:(v)=>{this._max=v;this.redraw()}});\n      this._step=this.getAttr(\"step\",1); Object.defineProperty(this,\"step\",{get:()=>{return this._step},set:(v)=>{this._step=v;this.redraw()}});\n      this._sprites=this.getAttr(\"sprites\",0); Object.defineProperty(this,\"sprites\",{get:()=>{return this._sprites},set:(v)=>{this._sprites=v;this.setupImage()}});\n      this._direction=this.getAttr(\"direction\",null); Object.defineProperty(this,\"direction\",{get:()=>{return this._direction},set:(v)=>{this._direction=v;this.setupImage()}});\n      this._width=this.getAttr(\"width\",opt.sliderWidth); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",opt.sliderHeight); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      if(this._direction==\"horz\"){\n        if(this._width==0) this._width=128;\n        if(this._height==0) this._height=24;\n      }\n      else{\n        if(this._width==0) this._width=24;\n        if(this._height==0) this._height=128;\n      }\n      this._knobwidth=this.getAttr(\"knobwidth\",opt.sliderKnobwidth); Object.defineProperty(this,\"knobwidth\",{get:()=>{return this._knobwidth},set:(v)=>{this._knobwidth=v;this.setupImage()}});\n      this._knobheight=this.getAttr(\"knbheight\",opt.sliderKnobheight); Object.defineProperty(this,\"knobheight\",{get:()=>{return this._knobheight},set:(v)=>{this._knobheight=v;this.setupImage()}});\n      this._ditchlength=this.getAttr(\"ditchlength\",opt.sliderDitchlength); Object.defineProperty(this,\"ditchlength\",{get:()=>{return this._ditchlength},set:(v)=>{this._ditchlength=v;this.setupImage()}});\n      this._colors=this.getAttr(\"colors\",opt.sliderColors); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.sensitivity=this.getAttr(\"sensitivity\",1);\n      this.valuetip=this.getAttr(\"valuetip\",1);\n      this.tooltip=this.getAttr(\"tooltip\",null);\n      this.conv=this.getAttr(\"conv\",null);\n      if(this.conv)\n        this.convValue=eval(this.conv)(this._value);\n      else\n        this.convValue=this._value;\n      this.midilearn=this.getAttr(\"midilearn\",opt.midilearn);\n      this.midicc=this.getAttr(\"midicc\",null);\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n          let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n          let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n          this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      this.digits=0;\n      if(window.webAudioControlsMidiManager)\n//        window.webAudioControlsMidiManager.updateWidgets();\n        window.webAudioControlsMidiManager.addWidget(this);\n      this.elem.onclick=(e)=>{e.stopPropagation()};\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      this.coltab = this.colors.split(\";\");\n      this.dr=this.direction;\n      this.dlen=this.ditchlength;\n      if(!this.width){\n        if(this.dr==\"horz\")\n          this.width=128;\n        else\n          this.width=24;\n      }\n      if(!this.height){\n        if(this.dr==\"horz\")\n          this.height=24;\n        else\n          this.height=128;\n      }\n      if(!this.dr)\n        this.dr=(this.width<=this.height)?\"vert\":\"horz\";\n      if(this.dr==\"vert\"){\n        if(!this.dlen)\n          this.dlen=this.height-this.width;\n      }\n      else{\n        if(!this.dlen)\n          this.dlen=this.width-this.height;\n      }\n      this.knob.style.backgroundSize = \"100% 100%\";\n      this.elem.style.backgroundSize = \"100% 100%\";\n      this.elem.style.width=this.width+\"px\";\n      this.elem.style.height=this.height+\"px\";\n      this.style.height=this.height+\"px\";\n      this.kwidth=this.knobwidth||(this.dr==\"horz\"?this.height:this.width);\n      this.kheight=this.knobheight||(this.dr==\"horz\"?this.height:this.width);\n      this.knob.style.width = this.kwidth+\"px\";\n      this.knob.style.height = this.kheight+\"px\";\n      if(!this.src){\n        let r=Math.min(this.width,this.height)*0.5;\n        let svgbody=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${this.width}\" height=\"${this.height}\" preserveAspectRatio=\"none\">\n<rect x=\"1\" y=\"1\" rx=\"${r}\" ry=\"${r}\" width=\"${this.width-2}\" height=\"${this.height-2}\" fill=\"${this.coltab[1]}\"/></svg>`;\n        this.elem.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svgbody)+\")\";\n      }\n      else{\n        this.elem.style.backgroundImage = \"url(\"+(this.src)+\")\";\n      }\n      if(!this.knobsrc){\n        let svgthumb=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${this.kwidth}\" height=\"${this.kheight}\" preserveAspectRatio=\"none\">\n<radialGradient id=\"gr\" cx=\"30%\" cy=\"30%\"><stop offset=\"0%\" stop-color=\"${this.coltab[2]}\"/><stop offset=\"100%\" stop-color=\"${this.coltab[0]}\"/></radialGradient>\n<rect x=\"2\" y=\"2\" width=\"${this.kwidth-4}\" height=\"${this.kheight-4}\" rx=\"${this.kwidth*0.5}\" ry=\"${this.kheight*0.5}\" fill=\"url(#gr)\"/></svg>`;\n        this.knob.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svgthumb)+\")\";\n      }\n      else{\n        this.knob.style.backgroundImage = \"url(\"+(this.knobsrc)+\")\";\n      }\n      this.elem.style.outline=this.outline?\"\":\"none\";\n      this.redraw();\n    }\n    redraw() {\n      this.digits=0;\n      if(this.step && this.step < 1) {\n        for(let n = this.step ; n < 1; n *= 10)\n          ++this.digits;\n      }\n      if(this.value<this.min){\n        this.value=this.min;\n        return;\n      }\n      if(this.value>this.max){\n        this.value=this.max;\n        return;\n      }\n      let range = this.max - this.min;\n      let style = this.knob.style;\n      if(this.dr==\"vert\"){\n        style.left=(this.width-this.kwidth)*0.5+\"px\";\n        style.top=(1-(this.value-this.min)/range)*this.dlen+\"px\";\n        this.sensex=0; this.sensey=1;\n      }\n      else{\n        style.top=(this.height-this.kheight)*0.5+\"px\";\n        style.left=(this.value-this.min)/range*this.dlen+\"px\";\n        this.sensex=1; this.sensey=0;\n      }\n    }\n    _setValue(v){\n      v=(Math.round((v-this.min)/this.step))*this.step+this.min;\n      this._value=Math.min(this.max,Math.max(this.min,v));\n      if(this._value!=this.oldvalue){\n        this.oldvalue=this._value;\n        if(this.conv)\n          this.convValue=eval(this.conv)(this._value);\n        else\n          this.convValue=this._value;\n        this.redraw();\n        this.showtip(0);\n        return 1;\n      }\n      return 0;\n    }\n    setValue(v,f){\n      if(this._setValue(v)&&f)\n        this.sendEvent(\"input\"),this.sendEvent(\"change\");\n    }\n    wheel(e) {\n      let delta=(this.max-this.min)*0.01;\n      delta=e.deltaY>0?-delta:delta;\n      if(!e.shiftKey)\n        delta*=5;\n      if(Math.abs(delta) < this.step)\n        delta = (delta > 0) ? +this.step : -this.step;\n      this.setValue(+this.value+delta,true);\n      e.preventDefault();\n      e.stopPropagation();\n      this.redraw();\n    }\n    pointerdown(ev){\n      if(!this.enable)\n        return;\n      let e=ev;\n      if(ev.touches){\n        e = ev.changedTouches[0];\n        this.identifier=e.identifier;\n      }\n      else {\n        if(e.buttons!=1 && e.button!=0)\n          return;\n      }\n      this.elem.focus();\n      this.drag=1;\n      this.showtip(0);\n      let pointermove=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;i<ev.touches.length;++i){\n            if(ev.touches[i].identifier==this.identifier){\n              e = ev.touches[i];\n              break;\n            }\n          }\n        }\n        if(this.lastShift !== e.shiftKey) {\n          this.lastShift = e.shiftKey;\n          this.startPosX = e.pageX;\n          this.startPosY = e.pageY;\n          this.startVal = this.value;\n        }\n        let offset = ((this.startPosY - e.pageY)*this.sensey - (this.startPosX - e.pageX)*this.sensex) * this.sensitivity;\n        this._setValue(this.min + ((((this.startVal + (this.max - this.min) * offset / ((e.shiftKey ? 4 : 1) * this.dlen)) - this.min) / this.step) | 0) * this.step);\n        this.sendEvent(\"input\");\n        if(e.preventDefault)\n          e.preventDefault();\n        if(e.stopPropagation)\n          e.stopPropagation();\n        return false;\n      }\n      let pointerup=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;;){\n            if(ev.changedTouches[i].identifier==this.identifier){\n              break;\n            }\n            if(++i>=ev.changedTouches.length)\n              return;\n          }\n        }\n        this.drag=0;\n        this.showtip(0);\n        this.startPosX = this.startPosY = null;\n        window.removeEventListener('mousemove', pointermove);\n        window.removeEventListener('touchmove', pointermove, {passive:false});\n        window.removeEventListener('mouseup', pointerup);\n        window.removeEventListener('touchend', pointerup);\n        window.removeEventListener('touchcancel', pointerup);\n        document.body.removeEventListener('touchstart', preventScroll,{passive:false});\n        this.sendEvent(\"change\");\n      }\n      let preventScroll=(e)=>{\n        e.preventDefault();\n      }\n      if(e.touches)\n        e = e.touches[0];\n      if(e.ctrlKey || e.metaKey)\n        this.setValue(this.defvalue,true);\n      else {\n        this.startPosX = e.pageX;\n        this.startPosY = e.pageY;\n        this.startVal = this.value;\n        window.addEventListener('mousemove', pointermove);\n        window.addEventListener('touchmove', pointermove, {passive:false});\n      }\n      window.addEventListener('mouseup', pointerup);\n      window.addEventListener('touchend', pointerup);\n      window.addEventListener('touchcancel', pointerup);\n      document.body.addEventListener('touchstart', preventScroll,{passive:false});\n      e.preventDefault();\n      e.stopPropagation();\n      return false;\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-slider already defined\");\n}\n\ntry{\n  customElements.define(\"webaudio-switch\", class WebAudioSwitch extends WebAudioControlsWidget {\n    constructor(){\n      super();\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-switch{\n  display:inline-block;\n  margin:0;\n  padding:0;\n  font-family: sans-serif;\n  font-size: 11px;\n  cursor:pointer;\n}\n.webaudio-switch-body{\n  display:inline-block;\n  margin:0;\n  padding:0;\n  vertical-align:bottom;\n}\n</style>\n<div class='webaudio-switch-body' tabindex='1' touch-action='none'><div class='webaudioctrl-tooltip'></div></div>\n`;\n      this.elem=root.childNodes[2];\n      this.ttframe=this.elem.childNodes[0];\n\n      this.enable=this.getAttr(\"enable\",1);\n      this._src=this.getAttr(\"src\",null); Object.defineProperty(this,\"src\",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}});\n      this._value=this.getAttr(\"value\",0); Object.defineProperty(this,\"value\",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}});\n      this.defvalue=this.getAttr(\"defvalue\",0);\n      this.type=this.getAttr(\"type\",\"toggle\");\n      this.group=this.getAttr(\"group\",\"\");\n      this._width=this.getAttr(\"width\",0); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",0); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      this._diameter=this.getAttr(\"diameter\",0); Object.defineProperty(this,\"diameter\",{get:()=>{return this._diameter},set:(v)=>{this._diameter=v;this.setupImage()}});\n      this.invert=this.getAttr(\"invert\",0);\n      this._colors=this.getAttr(\"colors\",opt.switchColors); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.valuetip=0;\n      this.tooltip=this.getAttr(\"tooltip\",null);\n      this.midilearn=this.getAttr(\"midilearn\",opt.midilearn);\n      this.midicc=this.getAttr(\"midicc\",null);\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n          let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n          let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n          this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      this.digits=0;\n      if(window.webAudioControlsMidiManager)\n//        window.webAudioControlsMidiManager.updateWidgets();\n        window.webAudioControlsMidiManager.addWidget(this);\n      this.elem.onclick=(e)=>{e.stopPropagation()};\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      let w=this.width||this.diameter||opt.switchWidth||opt.switchDiameter;\n      let h=this.height||this.diameter||opt.switchHeight||opt.switchDiameter;\n      if(!this.src){\n        this.coltab = this.colors.split(\";\");\n        let mm=Math.min(w,h);\n        let svg=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${w}\" height=\"${h*2}\" preserveAspectRatio=\"none\">\n<radialGradient id=\"gr\" cx=\"30%\" cy=\"30%\"><stop offset=\"0%\" stop-color=\"${this.coltab[2]}\"/><stop offset=\"100%\" stop-color=\"${this.coltab[0]}\"/></radialGradient>\n<rect x=\"${w*0.05}\" y=\"${h*0.05}\" width=\"${w*0.9}\" height=\"${h*0.9}\" rx=\"${mm*0.1}\" ry=\"${mm*0.1}\" fill=\"${this.coltab[1]}\"/>\n<rect x=\"${w*0.05}\" y=\"${h*1.05}\" width=\"${w*0.9}\" height=\"${h*0.9}\" rx=\"${mm*0.1}\" ry=\"${mm*0.1}\" fill=\"${this.coltab[1]}\"/>\n<circle cx=\"${w*0.5}\" cy=\"${h*0.5}\" r=\"${mm*0.3}\" stroke=\"${this.coltab[0]}\" stroke-width=\"2\"/>\n<circle cx=\"${w*0.5}\" cy=\"${h*1.5}\" r=\"${mm*0.3}\" stroke=\"${this.coltab[0]}\" stroke-width=\"2\" fill=\"url(#gr)\"/></svg>`;\n        this.elem.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svg)+\")\";\n        this.elem.style.backgroundSize = \"100% 200%\";\n      }\n      else{\n        this.elem.style.backgroundImage = \"url(\"+(this.src)+\")\";\n        if(!this.sprites)\n          this.elem.style.backgroundSize = \"100% 200%\";\n        else\n          this.elem.style.backgroundSize = `100% ${(this.sprites+1)*100}%`;\n      }\n      this.elem.style.width=w+\"px\";\n      this.elem.style.height=h+\"px\";\n      this.style.height=h+\"px\";\n      this.elem.style.outline=this.outline?\"\":\"none\";\n      this.redraw();\n    }\n    redraw() {\n      let style = this.elem.style;\n      if(this.value^this.invert)\n        style.backgroundPosition = \"0px -100%\";\n      else\n        style.backgroundPosition = \"0px 0px\";\n    }\n    setValue(v,f){\n      this.value=v;\n      this.checked=(!!v);\n      if(this.value!=this.oldvalue){\n        this.redraw();\n        this.showtip(0);\n        if(f){\n          this.sendEvent(\"input\");\n          this.sendEvent(\"change\");\n        }\n        this.oldvalue=this.value;\n      }\n    }\n    pointerdown(ev){\n      if(!this.enable)\n        return;\n      let e=ev;\n      if(ev.touches){\n        e = ev.changedTouches[0];\n        this.identifier=e.identifier;\n      }\n      else {\n        if(e.buttons!=1 && e.button!=0)\n          return;\n      }\n      this.elem.focus();\n      this.drag=1;\n      this.showtip(0);\n      let pointermove=(e)=>{\n        e.preventDefault();\n        e.stopPropagation();\n        return false;\n      }\n      let pointerup=(e)=>{\n        this.drag=0;\n        this.showtip(0);\n        window.removeEventListener('mousemove', pointermove);\n        window.removeEventListener('touchmove', pointermove, {passive:false});\n        window.removeEventListener('mouseup', pointerup);\n        window.removeEventListener('touchend', pointerup);\n        window.removeEventListener('touchcancel', pointerup);\n        document.body.removeEventListener('touchstart', preventScroll,{passive:false});\n        if(this.type==\"kick\"){\n          this.value=0;\n          this.checked=false;\n          this.redraw();\n          this.sendEvent(\"change\");\n        }\n        this.sendEvent(\"click\");\n        e.preventDefault();\n        e.stopPropagation();\n      }\n      let preventScroll=(e)=>{\n        e.preventDefault();\n      }\n      switch(this.type){\n      case \"kick\":\n        this.setValue(1);\n        this.sendEvent(\"change\");\n        break;\n      case \"toggle\":\n        if(e.ctrlKey || e.metaKey)\n          this.value=defvalue;\n        else\n          this.value=1-this.value;\n        this.checked=!!this.value;\n        this.sendEvent(\"change\");\n        break;\n      case \"radio\":\n        let els=document.querySelectorAll(\"webaudio-switch[type='radio'][group='\"+this.group+\"']\");\n        for(let i=0;i<els.length;++i){\n          if(els[i]==this)\n            els[i].setValue(1);\n          else\n            els[i].setValue(0);\n        }\n        this.sendEvent(\"change\");\n        break;\n      }\n\n      window.addEventListener('mouseup', pointerup);\n      window.addEventListener('touchend', pointerup);\n      window.addEventListener('touchcancel', pointerup);\n      document.body.addEventListener('touchstart', preventScroll,{passive:false});\n      this.redraw();\n      e.preventDefault();\n      e.stopPropagation();\n      return false;\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-switch already defined\");\n}\n\ntry{\n  customElements.define(\"webaudio-param\", class WebAudioParam extends WebAudioControlsWidget {\n    constructor(){\n      super();\n      this.addEventListener(\"keydown\",this.keydown);\n      this.addEventListener(\"mousedown\",this.pointerdown,{passive:false});\n      this.addEventListener(\"touchstart\",this.pointerdown,{passive:false});\n      this.addEventListener(\"wheel\",this.wheel);\n      this.addEventListener(\"mouseover\",this.pointerover);\n      this.addEventListener(\"mouseout\",this.pointerout);\n      this.addEventListener(\"contextmenu\",this.contextMenu);\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-param{\n  display:inline-block;\n  user-select:none;\n  margin:0;\n  padding:0;\n  font-family: sans-serif;\n  font-size: 8px;\n  cursor:pointer;\n  position:relative;\n  vertical-align:baseline;\n}\n.webaudio-param-body{\n  display:inline-block;\n  position:relative;\n  text-align:center;\n  border:1px solid #888;\n  background:none;\n  border-radius:4px;\n  margin:0;\n  padding:0;\n  font-family:sans-serif;\n  font-size:11px;\n  vertical-align:bottom;\n}\n</style>\n<input class='webaudio-param-body' value='0' tabindex='1' touch-action='none'/><div class='webaudioctrl-tooltip'></div>\n`;\n      this.elem=root.childNodes[2];\n      this.ttframe=root.childNodes[3];\n      this.enable=this.getAttr(\"enable\",1);\n      this._value=this.getAttr(\"value\",0); Object.defineProperty(this,\"value\",{get:()=>{return this._value},set:(v)=>{this._value=v;this.redraw()}});\n      this.defvalue=this.getAttr(\"defvalue\",0);\n      this._fontsize=this.getAttr(\"fontsize\",9); Object.defineProperty(this,\"fontsize\",{get:()=>{return this._fontsize},set:(v)=>{this._fontsize=v;this.setupImage()}});\n      this._src=this.getAttr(\"src\",null); Object.defineProperty(this,\"src\",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}});\n      this.link=this.getAttr(\"link\",\"\");\n      this._width=this.getAttr(\"width\",32); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",20); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      this._colors=this.getAttr(\"colors\",\"#fff;#000\"); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n        let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n        let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n        this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      if(window.webAudioControlsMidiManager)\n//        window.webAudioControlsMidiManager.updateWidgets();\n        window.webAudioControlsMidiManager.addWidget(this);\n      this.fromLink=((e)=>{\n        this.setValue(e.target.convValue.toFixed(e.target.digits));\n      }).bind(this);\n      this.elem.onchange=()=>{\n        this.value=this.elem.value;\n        let le=document.getElementById(this.link);\n        if(le)\n          le.setValue(+this.elem.value);\n      }\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      this.coltab = this.colors.split(\";\");\n      this.elem.style.color=this.coltab[0];\n      if(!this.src){\n        this.elem.style.backgroundColor=this.coltab[1];\n      }\n      else{\n        this.elem.style.backgroundImage = \"url(\"+(this.src)+\")\";\n        this.elem.style.backgroundSize = \"100% 100%\";\n      }\n      this.elem.style.width=this.width+\"px\";\n      this.elem.style.height=this.height+\"px\";\n      this.elem.style.fontSize=this.fontsize+\"px\";\n      this.elem.style.outline=this.outline?\"\":\"none\";\n      let l=document.getElementById(this.link);\n      if(l&&typeof(l.value)!=\"undefined\"){\n        this.setValue(l.value.toFixed(l.digits));\n        l.addEventListener(\"input\",(e)=>{this.setValue(l.value.toFixed(l.digits))});\n      }\n      this.redraw();\n    }\n    redraw() {\n      this.elem.value=this.value;\n    }\n    setValue(v,f){\n      this.value=v;\n      if(this.value!=this.oldvalue){\n        this.redraw();\n        this.showtip(0);\n        if(f){\n          let event=document.createEvent(\"HTMLEvents\");\n          event.initEvent(\"change\",false,true);\n          this.dispatchEvent(event);\n        }\n        this.oldvalue=this.value;\n      }\n    }\n    pointerdown(ev){\n      if(!this.enable)\n        return;\n      let e=ev;\n      if(ev.touches)\n          e = ev.touches[0];\n      else {\n        if(e.buttons!=1 && e.button!=0)\n          return;\n      }\n      this.elem.focus();\n      this.redraw();\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-param already defined\");\n}\n\ntry{\n  customElements.define(\"webaudio-keyboard\", class WebAudioKeyboard extends WebAudioControlsWidget {\n    constructor(){\n      super();\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-keyboard{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  font-family: sans-serif;\n  font-size: 11px;\n}\n.webaudio-keyboard-body{\n  display:inline-block;\n  margin:0;\n  padding:0;\n  vertical-align:bottom;\n}\n</style>\n<canvas class='webaudio-keyboard-body' tabindex='1' touch-action='none'></canvas><div class='webauioctrl-tooltip'></div>\n`;\n      this.cv=root.childNodes[2];\n      this.ttframe=root.childNodes[3];\n      this.ctx=this.cv.getContext(\"2d\");\n      this._values=[];\n      this.enable=this.getAttr(\"enable\",1);\n      this._width=this.getAttr(\"width\",480); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",128); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      this._min=this.getAttr(\"min\",0); Object.defineProperty(this,\"min\",{get:()=>{return this._min},set:(v)=>{this._min=+v;this.redraw()}});\n      this._keys=this.getAttr(\"keys\",25); Object.defineProperty(this,\"keys\",{get:()=>{return this._keys},set:(v)=>{this._keys=+v;this.setupImage()}});\n      this._colors=this.getAttr(\"colors\",\"#222;#eee;#ccc;#333;#000;#e88;#c44;#c33;#800\"); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.midilearn=this.getAttr(\"midilearn\",0);\n      this.midicc=this.getAttr(\"midicc\",null);\n      this.press=0;\n      this.keycodes1=[90,83,88,68,67,86,71,66,72,78,74,77,188,76,190,187,191,226];\n      this.keycodes2=[81,50,87,51,69,82,53,84,54,89,55,85,73,57,79,48,80,192,222,219];\n      this.addEventListener(\"keyup\",this.keyup);\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n          let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n          let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n          this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      this.digits=0;\n      if(window.webAudioControlsMidiManager)\n        window.webAudioControlsMidiManager.addWidget(this);\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      this.cv.style.width=this.width+\"px\";\n      this.cv.style.height=this.height+\"px\";\n      this.bheight = this.height * 0.55;\n      this.kp=[0,7/12,1,3*7/12,2,3,6*7/12,4,8*7/12,5,10*7/12,6];\n      this.kf=[0,1,0,1,0,0,1,0,1,0,1,0];\n      this.ko=[0,0,(7*2)/12-1,0,(7*4)/12-2,(7*5)/12-3,0,(7*7)/12-4,0,(7*9)/12-5,0,(7*11)/12-6];\n      this.kn=[0,2,4,5,7,9,11];\n      this.coltab=this.colors.split(\";\");\n      this.cv.width = this.width;\n      this.cv.height = this.height;\n      this.cv.style.width = this.width+'px';\n      this.cv.style.height = this.height+'px';\n      this.style.height = this.height+'px';\n      this.cv.style.outline=this.outline?\"\":\"none\";\n      this.bheight = this.height * 0.55;\n      this.max=this.min+this.keys-1;\n      this.dispvalues=[];\n      this.valuesold=[];\n      if(this.kf[this.min%12])\n        --this.min;\n      if(this.kf[this.max%12])\n        ++this.max;\n      this.redraw();\n    }\n    redraw(){\n      function rrect(ctx, x, y, w, h, r, c1, c2) {\n        if(c2) {\n          let g=ctx.createLinearGradient(x,y,x+w,y);\n          g.addColorStop(0,c1);\n          g.addColorStop(1,c2);\n          ctx.fillStyle=g;\n        }\n        else\n          ctx.fillStyle=c1;\n        ctx.beginPath();\n        ctx.moveTo(x, y);\n        ctx.lineTo(x+w, y);\n        ctx.lineTo(x+w, y+h-r);\n        ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);\n        ctx.lineTo(x+r, y+h);\n        ctx.quadraticCurveTo(x, y+h, x, y+h-r);\n        ctx.lineTo(x, y);\n        ctx.fill();\n      }\n      this.ctx.fillStyle = this.coltab[0];\n      this.ctx.fillRect(0,0,this.width,this.height);\n      let x0=7*((this.min/12)|0)+this.kp[this.min%12];\n      let x1=7*((this.max/12)|0)+this.kp[this.max%12];\n      let n=x1-x0;\n      this.wwidth=(this.width-1)/(n+1);\n      this.bwidth=this.wwidth*7/12;\n      let h2=this.bheight;\n      let r=Math.min(8,this.wwidth*0.2);\n      for(let i=this.min,j=0;i<=this.max;++i) {\n        if(this.kf[i%12]==0) {\n          let x=this.wwidth*(j++)+1;\n          if(this.dispvalues.indexOf(i)>=0)\n            rrect(this.ctx,x,1,this.wwidth-1,this.height-2,r,this.coltab[5],this.coltab[6]);\n          else\n            rrect(this.ctx,x,1,this.wwidth-1,this.height-2,r,this.coltab[1],this.coltab[2]);\n        }\n      }\n      r=Math.min(8,this.bwidth*0.3);\n      for(let i=this.min;i<this.max;++i) {\n        if(this.kf[i%12]) {\n          let x=this.wwidth*this.ko[this.min%12]+this.bwidth*(i-this.min)+1;\n          if(this.dispvalues.indexOf(i)>=0)\n            rrect(this.ctx,x,1,this.bwidth,h2,r,this.coltab[7],this.coltab[8]);\n          else\n            rrect(this.ctx,x,1,this.bwidth,h2,r,this.coltab[3],this.coltab[4]);\n          this.ctx.strokeStyle=this.coltab[0];\n          this.ctx.stroke();\n        }\n      }\n    }\n    _setValue(v){\n      if(this.step)\n        v=(Math.round((v-this.min)/this.step))*this.step+this.min;\n      this._value=Math.min(this.max,Math.max(this.min,v));\n      if(this._value!=this.oldvalue){\n        this.oldvalue=this._value;\n        this.redraw();\n        this.showtip(0);\n        return 1;\n      }\n      return 0;\n    }\n    setValue(v,f){\n      if(this._setValue(v) && f)\n        this.sendEvent(\"input\"),this.sendEvent(\"change\");\n    }\n    wheel(e){}\n    keydown(e){\n      let m=Math.floor((this.min+11)/12)*12;\n      let k=this.keycodes1.indexOf(e.keyCode);\n      if(k<0) {\n        k=this.keycodes2.indexOf(e.keyCode);\n        if(k>=0) k+=12;\n      }\n      if(k>=0){\n        k+=m;\n        if(this.currentKey!=k){\n          this.currentKey=k;\n          this.sendEventFromKey(1,k);\n          this.setNote(1,k);\n        }\n      }\n    }\n    keyup(e){\n      let m=Math.floor((this.min+11)/12)*12;\n      let k=this.keycodes1.indexOf(e.keyCode);\n      if(k<0) {\n        k=this.keycodes2.indexOf(e.keyCode);\n        if(k>=0) k+=12;\n      }\n      if(k>=0){\n        k+=m;\n        this.currentKey=-1;\n        this.sendEventFromKey(0,k);\n        this.setNote(0,k);\n      }\n    }\n    pointerdown(ev){\n      this.cv.focus();\n      if(this.enable) {\n        ++this.press;\n      }\n      let pointermove=(ev)=>{\n        if(!this.enable)\n          return;\n        let r=this.getBoundingClientRect();\n        let v=[],p;\n        if(ev.touches)\n          p=ev.targetTouches;\n        else if(this.press)\n          p=[ev];\n        else\n          p=[];\n        if(p.length>0)\n          this.drag=1;\n        for(let i=0;i<p.length;++i) {\n          let px=p[i].clientX-r.left;\n          let py=p[i].clientY-r.top;\n          let x,k,ko;\n          if(py>=0&&py<this.height){\n            if(py<this.bheight) {\n              x=px-this.wwidth*this.ko[this.min%12];\n              k=this.min+((x/this.bwidth)|0);\n            }\n            else {\n              k=(px/this.wwidth)|0;\n              ko=this.kp[this.min%12];\n              k+=ko;\n              k=this.min+((k/7)|0)*12+this.kn[k%7]-this.kn[ko%7];\n            }\n            if(k>=this.min&&k<=this.max)\n              v.push(k);\n          }\n        }\n        v.sort();\n        this.values=v;\n        this.sendevent();\n        this.redraw();\n      }\n        \n      let pointerup=(ev)=>{\n        if(this.enable) {\n          if(ev.touches)\n            this.press=ev.touches.length;\n          else\n            this.press=0;\n          pointermove(ev);\n          this.sendevent();\n          if(this.press==0){\n            window.removeEventListener('mousemove', pointermove);\n            window.removeEventListener('touchmove', pointermove, {passive:false});\n            window.removeEventListener('mouseup', pointerup);\n            window.removeEventListener('touchend', pointerup);\n            window.removeEventListener('touchcancel', pointerup);\n            document.body.removeEventListener('touchstart', preventScroll,{passive:false});\n          }\n          this.redraw();\n        }\n        this.drag=0;\n        ev.preventDefault();\n      }\n      let preventScroll=(ev)=>{\n        ev.preventDefault();\n      }\n      window.addEventListener('mousemove', pointermove);\n      window.addEventListener('touchmove', pointermove, {passive:false});\n      window.addEventListener('mouseup', pointerup);\n      window.addEventListener('touchend', pointerup);\n      window.addEventListener('touchcancel', pointerup);\n      document.body.addEventListener('touchstart', preventScroll,{passive:false});\n      pointermove(ev);\n      ev.preventDefault();\n      ev.stopPropagation();\n    }\n    sendEventFromKey(s,k){\n      let ev=document.createEvent('HTMLEvents');\n      ev.initEvent('change',true,true);\n      ev.note=[s,k];\n      this.dispatchEvent(ev);\n    }\n    sendevent(){\n      let notes=[];\n      for(let i=0,j=this.valuesold.length;i<j;++i) {\n        if(this.values.indexOf(this.valuesold[i])<0)\n          notes.push([0,this.valuesold[i]]);\n      }\n      for(let i=0,j=this.values.length;i<j;++i) {\n        if(this.valuesold.indexOf(this.values[i])<0)\n          notes.push([1,this.values[i]]);\n      }\n      if(notes.length) {\n        this.valuesold=this.values;\n        for(let i=0;i<notes.length;++i) {\n          this.setdispvalues(notes[i][0],notes[i][1]);\n          let ev=document.createEvent('HTMLEvents');\n          ev.initEvent('change',true,true);\n          ev.note=notes[i];\n          this.dispatchEvent(ev);\n        }\n      }\n    }\n    setdispvalues(state,note) {\n      let n=this.dispvalues.indexOf(note);\n      if(state) {\n        if(n<0) this.dispvalues.push(note);\n      }\n      else {\n        if(n>=0) this.dispvalues.splice(n,1);\n      }\n    }\n    setNote(state,note) {\n      this.setdispvalues(state,note);\n      this.redraw();\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-keyboard already defined\");\n}\n\ntry{\n  customElements.define(\"webaudio-xypad\", class WebAudioXYPad extends WebAudioControlsWidget {\n    constructor(){\n      super();\n    }\n    connectedCallback(){\n      let root;\n//      if(this.attachShadow)\n//        root=this.attachShadow({mode: 'open'});\n//      else\n        root=this;\n      root.innerHTML=\n`<style>\n${this.basestyle}\nwebaudio-xypad{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  font-family: sans-serif;\n  font-size: 11px;\n  cursor:pointer;\n}\n.webaudio-xypad-body{\n  display:inline-block;\n  position:relative;\n  margin:0;\n  padding:0;\n  vertical-align:bottom;\n}\n.webaudio-xypad-knob{\n  display:inline-block;\n  position:absolute;\n  margin:0;\n  padding:0;\n}\n</style>\n<div class='webaudio-xypad-body' tabindex='1' touch-action='none'><div class='webaudio-xypad-knob' touch-action='none'></div></div><div class='webaudioctrl-tooltip'></div>\n`;\n      this.elem=root.childNodes[2];\n      this.knob=this.elem.childNodes[0];\n      this.ttframe=root.childNodes[3];\n\n      this.enable=this.getAttr(\"enable\",1);\n      this._src=this.getAttr(\"src\",opt.sliderSrc); Object.defineProperty(this,\"src\",{get:()=>{return this._src},set:(v)=>{this._src=v;this.setupImage()}});\n      this._knobsrc=this.getAttr(\"knobsrc\",opt.sliderKnobsrc); Object.defineProperty(this,\"knobsrc\",{get:()=>{return this._knobsrc},set:(v)=>{this._knobsrc=v;this.setupImage()}});\n      this._x=this.getAttr(\"x\",50); Object.defineProperty(this,\"x\",{get:()=>{return this._x},set:(v)=>{this._x=v;this.redraw()}});\n      this._y=this.getAttr(\"y\",50); Object.defineProperty(this,\"y\",{get:()=>{return this._y},set:(v)=>{this._y=v;this.redraw()}});\n      this.defx=this.getAttr(\"defx\",50);\n      this.defy=this.getAttr(\"defy\",50);\n      this._min=this.getAttr(\"min\",0); Object.defineProperty(this,\"min\",{get:()=>{return this._min},set:(v)=>{this._min=v;this.redraw()}});\n      this._max=this.getAttr(\"max\",100); Object.defineProperty(this,\"max\",{get:()=>{return this._max},set:(v)=>{this._max=v;this.redraw()}});\n      this._step=this.getAttr(\"step\",1); Object.defineProperty(this,\"step\",{get:()=>{return this._step},set:(v)=>{this._step=v;this.redraw()}});\n      this._sprites=this.getAttr(\"sprites\",0); Object.defineProperty(this,\"sprites\",{get:()=>{return this._sprites},set:(v)=>{this._sprites=v;this.setupImage()}});\n      this._width=this.getAttr(\"width\",128); Object.defineProperty(this,\"width\",{get:()=>{return this._width},set:(v)=>{this._width=v;this.setupImage()}});\n      this._height=this.getAttr(\"height\",128); Object.defineProperty(this,\"height\",{get:()=>{return this._height},set:(v)=>{this._height=v;this.setupImage()}});\n      this._knobwidth=this.getAttr(\"knobwidth\",28); Object.defineProperty(this,\"knobwidth\",{get:()=>{return this._knobwidth},set:(v)=>{this._knobwidth=v;this.setupImage()}});\n      this._knobheight=this.getAttr(\"knbheight\",28); Object.defineProperty(this,\"knobheight\",{get:()=>{return this._knobheight},set:(v)=>{this._knobheight=v;this.setupImage()}});\n      this._colors=this.getAttr(\"colors\",opt.sliderColors); Object.defineProperty(this,\"colors\",{get:()=>{return this._colors},set:(v)=>{this._colors=v;this.setupImage()}});\n      this.outline=this.getAttr(\"outline\",opt.outline);\n      this.valuetip=this.getAttr(\"valuetip\",1);\n      this.tooltip=this.getAttr(\"tooltip\",null);\n      this.conv=this.getAttr(\"conv\",null);\n      if(this.conv){\n        this.convValue={x:eval(this.conv)(this._x),y:eval(this.conv)(this._y)};\n      }\n      else\n        this.convValue={x:this._x,y:this._y};\n      this.midilearn=this.getAttr(\"midilearn\",opt.midilearn);\n      this.midicc=this.getAttr(\"midicc\",null);\n      this.midiController={};\n      this.midiMode=\"normal\";\n      if(this.midicc) {\n          let ch = parseInt(this.midicc.substring(0, this.midicc.lastIndexOf(\".\"))) - 1;\n          let cc = parseInt(this.midicc.substring(this.midicc.lastIndexOf(\".\") + 1));\n          this.setMidiController(ch, cc);\n      }\n      this.setupImage();\n      this.digits=0;\n      if(window.webAudioControlsMidiManager)\n//        window.webAudioControlsMidiManager.updateWidgets();\n        window.webAudioControlsMidiManager.addWidget(this);\n      this.elem.onclick=(e)=>{e.stopPropagation()};\n    }\n    disconnectedCallback(){}\n    setupImage(){\n      this.coltab = this.colors.split(\";\");\n      this.dr=this.direction;\n      this.dlen=this.ditchlength;\n      if(!this.width)\n        this.width=256;\n      if(!this.height)\n        this.height=256;\n      this.knob.style.backgroundSize = \"100% 100%\";\n      this.elem.style.backgroundSize = \"100% 100%\";\n      this.elem.style.width=this.width+\"px\";\n      this.elem.style.height=this.height+\"px\";\n      this.kwidth=this.knobwidth||(this.width*0.15|0);\n      this.kheight=this.knobheight||(this.height*0.15|0);\n      this.knob.style.width = this.kwidth+\"px\";\n      this.knob.style.height = this.kheight+\"px\";\n      if(!this.src){\n        let r=Math.min(this.width,this.height)*0.02;\n        let svgbody=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${this.width}\" height=\"${this.height}\" preserveAspectRatio=\"none\">\n<rect x=\"1\" y=\"1\" rx=\"${r}\" ry=\"${r}\" width=\"${this.width-2}\" height=\"${this.height-2}\" fill=\"${this.coltab[1]}\"/></svg>`;\n        this.elem.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svgbody)+\")\";\n      }\n      else{\n        this.elem.style.backgroundImage = \"url(\"+(this.src)+\")\";\n      }\n      if(!this.knobsrc){\n        let svgthumb=\n`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${this.kwidth}\" height=\"${this.kheight}\" preserveAspectRatio=\"none\">\n<radialGradient id=\"gr\" cx=\"30%\" cy=\"30%\"><stop offset=\"0%\" stop-color=\"${this.coltab[2]}\"/><stop offset=\"100%\" stop-color=\"${this.coltab[0]}\"/></radialGradient>\n<rect x=\"2\" y=\"2\" width=\"${this.kwidth-4}\" height=\"${this.kheight-4}\" rx=\"${this.kwidth*0.5}\" ry=\"${this.kheight*0.5}\" fill=\"url(#gr)\"/></svg>`;\n        this.knob.style.backgroundImage = \"url(data:image/svg+xml;base64,\"+btoa(svgthumb)+\")\";\n      }\n      else{\n        this.knob.style.backgroundImage = \"url(\"+(this.knobsrc)+\")\";\n      }\n      this.elem.style.outline=this.outline?\"\":\"none\";\n      this.redraw();\n    }\n    redraw() {\n      this.digits=0;\n      if(this.step && this.step < 1) {\n        for(let n = this.step ; n < 1; n *= 10)\n          ++this.digits;\n      }\n      if(this.value<this.min){\n        this.value=this.min;\n        return;\n      }\n      if(this.value>this.max){\n        this.value=this.max;\n        return;\n      }\n      let range = this.max - this.min;\n      let style = this.knob.style;\n      style.left=(this.width-this.kwidth)*(this._x-this.min)/(this.max-this.min)+\"px\"; style.top=(this.height-this.kheight)*(1-(this._y-this.min)/(this.max-this.min))+\"px\";\n      this.sensex=0; this.sensey=1;\n    }\n    _setX(v){\n      v=(Math.round((v-this.min)/this.step))*this.step+this.min;\n      this._x=Math.min(this.max,Math.max(this.min,v));\n      if(this._x!=this.oldx){\n        this.oldx=this._x;\n        if(this.conv){\n          this.convValue={x:eval(this.conv)(this._x),y:eval(this.conv)(this._y)};\n        }\n        else\n          this.convValue={x:this._x,y:this._y};\n        this.redraw();\n        this.showtip(0);\n        return 1;\n      }\n      return 0;\n    }\n    _setY(v){\n      v=(Math.round((v-this.min)/this.step))*this.step+this.min;\n      this._y=Math.min(this.max,Math.max(this.min,v));\n      if(this._y!=this.oldy){\n        this.oldy=this._y;\n        if(this.conv){\n          this.convValue={x:eval(this.conv)(this._x),y:eval(this.conv)(this._y)};\n        }\n        else\n          this.convValue={x:this._x,y:this._y};\n        this.redraw();\n        this.showtip(0);\n        return 1;\n      }\n      return 0;\n    }\n    setX(v,f){\n      if(this._setX(v)&&f)\n        this.sendEvent(\"input\"),this.sendEvent(\"change\");\n    }\n    setY(v,f){\n      if(this._setY(v)&&f)\n        this.sendEvent(\"input\"),this.sendEvent(\"change\");\n    }\n    wheel(e) {\n      let delta=(this.max-this.min)*0.01;\n      delta=e.deltaY>0?-delta:delta;\n      if(!e.shiftKey)\n        delta*=5;\n      if(Math.abs(delta) < this.step)\n        delta = (delta > 0) ? +this.step : -this.step;\n      this.setValue(+this.value+delta,true);\n      e.preventDefault();\n      e.stopPropagation();\n      this.redraw();\n    }\n    pointerdown(ev){\n      if(!this.enable)\n        return;\n      let e=ev;\n      if(ev.touches){\n        e = ev.changedTouches[0];\n        this.identifier=e.identifier;\n      }\n      else {\n        if(e.buttons!=1 && e.button!=0)\n          return;\n      }\n      this.elem.focus();\n      this.drag=1;\n      this.showtip(0);\n      let pointermove=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;i<ev.touches.length;++i){\n            if(ev.touches[i].identifier==this.identifier){\n              e = ev.touches[i];\n              break;\n            }\n          }\n        }\n        if(this.lastShift !== e.shiftKey) {\n          this.lastShift = e.shiftKey;\n          this.startPosX = e.pageX;\n          this.startPosY = e.pageY;\n          this.startVal = this.value;\n        }\n        let offsetX = (e.pageX - this.startPosX);\n        let offsetY = (this.startPosY - e.pageY);\n        let rc=this.getBoundingClientRect();\n        this._setX(this.min+(this.max-this.min)*(e.pageX-rc.x-this.kwidth*.5)/(this.width-this.kwidth));\n        this._setY(this.min+(this.max-this.min)*(1-(e.pageY-rc.y-this.kheight*.5)/(this.height-this.kheight)));\n        this.sendEvent(\"input\");\n        if(e.preventDefault)\n          e.preventDefault();\n        if(e.stopPropagation)\n          e.stopPropagation();\n        return false;\n      }\n      let pointerup=(ev)=>{\n        let e=ev;\n        if(ev.touches){\n          for(let i=0;;){\n            if(ev.changedTouches[i].identifier==this.identifier){\n              break;\n            }\n            if(++i>=ev.changedTouches.length)\n              return;\n          }\n        }\n        this.drag=0;\n        this.showtip(0);\n        this.startPosX = this.startPosY = null;\n        window.removeEventListener('mousemove', pointermove);\n        window.removeEventListener('touchmove', pointermove, {passive:false});\n        window.removeEventListener('mouseup', pointerup);\n        window.removeEventListener('touchend', pointerup);\n        window.removeEventListener('touchcancel', pointerup);\n        document.body.removeEventListener('touchstart', preventScroll,{passive:false});\n        this.sendEvent(\"change\");\n      }\n      pointermove(ev);\n      let preventScroll=(e)=>{\n        e.preventDefault();\n      }\n      if(e.touches)\n        e = e.touches[0];\n      if(e.ctrlKey || e.metaKey)\n        this.setValue(this.defvalue,true);\n      else {\n        this.startPosX = e.pageX;\n        this.startPosY = e.pageY;\n        this.startVal = this.value;\n        window.addEventListener('mousemove', pointermove);\n        window.addEventListener('touchmove', pointermove, {passive:false});\n      }\n      window.addEventListener('mouseup', pointerup);\n      window.addEventListener('touchend', pointerup);\n      window.addEventListener('touchcancel', pointerup);\n      document.body.addEventListener('touchstart', preventScroll,{passive:false});\n      e.preventDefault();\n      e.stopPropagation();\n      return false;\n    }\n  });\n} catch(error){\n  console.log(\"webaudio-xypad already defined\");\n}\n\n\n\n  // FOR MIDI LEARN\n  class WebAudioControlsMidiManager {\n    constructor(){\n      this.midiAccess = null;\n      this.listOfWidgets = [];\n      this.listOfExternalMidiListeners = [];\n      this.updateWidgets();\n      this.initWebAudioControls();\n    }\n    addWidget(w){\n      this.listOfWidgets.push(w);\n    }\n    updateWidgets(){\n//      this.listOfWidgets = document.querySelectorAll(\"webaudio-knob,webaudio-slider,webaudio-switch\");\n    }\n    initWebAudioControls() {\n      if(navigator.requestMIDIAccess) {\n        navigator.requestMIDIAccess().then(\n          (ma)=>{this.midiAccess = ma,this.enableInputs()},\n          (err)=>{ console.log(\"MIDI not initialized - error encountered:\" + err.code)}\n        );\n      }\n    }\n    enableInputs() {\n      let inputs = this.midiAccess.inputs.values();\n      console.log(\"Found \" + this.midiAccess.inputs.size + \" MIDI input(s)\");\n      for(let input = inputs.next(); input && !input.done; input = inputs.next()) {\n        console.log(\"Connected input: \" + input.value.name);\n        input.value.onmidimessage = this.handleMIDIMessage.bind(this);\n      }\n    }\n    midiConnectionStateChange(e) {\n      console.log(\"connection: \" + e.port.name + \" \" + e.port.connection + \" \" + e.port.state);\n      enableInputs();\n    }\n\n    onMIDIStarted(midi) {\n      this.midiAccess = midi;\n      midi.onstatechange = this.midiConnectionStateChange;\n      enableInputs(midi);\n    }\n    // Add hooks for external midi listeners support\n    addMidiListener(callback) {\n      this.listOfExternalMidiListeners.push(callback);\n    }\n    getCurrentConfigAsJSON() {\n      return currentConfig.stringify();\n    }\n    handleMIDIMessage(event) {\n      this.listOfExternalMidiListeners.forEach(function (externalListener) {\n        externalListener(event);\n      });\n      if(((event.data[0] & 0xf0) == 0xf0) || ((event.data[0] & 0xf0) == 0xb0 && event.data[1] >= 120))\n        return;\n      for(let w of this.listOfWidgets) {\n        if(w.processMidiEvent)\n          w.processMidiEvent(event);\n      }\n      if(opt.mididump)\n        console.log(event.data);\n    }\n    contextMenuOpen(e,knob){\n      if(!this.midiAccess)\n        return;\n      let menu=document.getElementById(\"webaudioctrl-context-menu\");\n      menu.style.left=e.pageX+\"px\";\n      menu.style.top=e.pageY+\"px\";\n      menu.knob=knob;\n      menu.classList.add(\"active\");\n      menu.knob.focus();\n//      document.activeElement.onblur=this.contextMenuClose;\n      menu.knob.addEventListener(\"keydown\",this.contextMenuCloseByKey.bind(this));\n    }\n    contextMenuCloseByKey(e){\n      if(e.keyCode==27)\n       this.contextMenuClose();\n    }\n    contextMenuClose(){\n      let menu=document.getElementById(\"webaudioctrl-context-menu\");\n      menu.knob.removeEventListener(\"keydown\",this.contextMenuCloseByKey);\n      menu.classList.remove(\"active\");\n      let menuItemLearn=document.getElementById(\"webaudioctrl-context-menu-learn\");\n      menuItemLearn.innerHTML = 'Learn';\n      menu.knob.midiMode = 'normal';\n    }\n    contextMenuLearn(){\n      let menu=document.getElementById(\"webaudioctrl-context-menu\");\n      let menuItemLearn=document.getElementById(\"webaudioctrl-context-menu-learn\");\n      menuItemLearn.innerHTML = 'Listening...';\n      menu.knob.midiMode = 'learn';\n    }\n    contextMenuClear(e){\n      let menu=document.getElementById(\"webaudioctrl-context-menu\");\n      menu.knob.midiController={};\n      this.contextMenuClose();\n    }\n  }\n  if(window.UseWebAudioControlsMidi||opt.useMidi)\n    window.webAudioControlsMidiManager = new WebAudioControlsMidiManager();\n}\n"
  },
  {
    "path": "src/assets/js/webcomponents-lite.js",
    "content": "(function(){/*\n\n Copyright (c) 2016 The Polymer Project Authors. All rights reserved.\n This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n Code distributed by Google as part of the polymer project is also\n subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\n'use strict';var p,q=\"undefined\"!=typeof window&&window===this?this:\"undefined\"!=typeof global&&null!=global?global:this,ba=\"function\"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};function ca(){ca=function(){};q.Symbol||(q.Symbol=da)}var da=function(){var a=0;return function(b){return\"jscomp_symbol_\"+(b||\"\")+a++}}();\nfunction ea(){ca();var a=q.Symbol.iterator;a||(a=q.Symbol.iterator=q.Symbol(\"iterator\"));\"function\"!=typeof Array.prototype[a]&&ba(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return fa(this)}});ea=function(){}}function fa(a){var b=0;return ha(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})}function ha(a){ea();a={next:a};a[q.Symbol.iterator]=function(){return this};return a}function ia(a){ea();var b=a[Symbol.iterator];return b?b.call(a):fa(a)}\nfunction ja(a){for(var b,c=[];!(b=a.next()).done;)c.push(b.value);return c}\n(function(){if(!function(){var a=document.createEvent(\"Event\");a.initEvent(\"foo\",!0,!0);a.preventDefault();return a.defaultPrevented}()){var a=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(a.call(this),Object.defineProperty(this,\"defaultPrevented\",{get:function(){return!0},configurable:!0}))}}var b=/Trident/.test(navigator.userAgent);if(!window.CustomEvent||b&&\"function\"!==typeof window.CustomEvent)window.CustomEvent=function(a,b){b=b||{};var c=document.createEvent(\"CustomEvent\");\nc.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c},window.CustomEvent.prototype=window.Event.prototype;if(!window.Event||b&&\"function\"!==typeof window.Event){var c=window.Event;window.Event=function(a,b){b=b||{};var c=document.createEvent(\"Event\");c.initEvent(a,!!b.bubbles,!!b.cancelable);return c};if(c)for(var d in c)window.Event[d]=c[d];window.Event.prototype=c.prototype}if(!window.MouseEvent||b&&\"function\"!==typeof window.MouseEvent){b=window.MouseEvent;window.MouseEvent=function(a,\nb){b=b||{};var c=document.createEvent(\"MouseEvent\");c.initMouseEvent(a,!!b.bubbles,!!b.cancelable,b.view||window,b.detail,b.screenX,b.screenY,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};if(b)for(d in b)window.MouseEvent[d]=b[d];window.MouseEvent.prototype=b.prototype}Array.from||(Array.from=function(a){return[].slice.call(a)});Object.assign||(Object.assign=function(a,b){for(var c=[].slice.call(arguments,1),d=0,e;d<c.length;d++)if(e=c[d])for(var f=\na,m=e,n=Object.getOwnPropertyNames(m),t=0;t<n.length;t++)e=n[t],f[e]=m[e];return a})})(window.WebComponents);(function(){function a(){}function b(a,b){if(!a.childNodes.length)return[];switch(a.nodeType){case Node.DOCUMENT_NODE:return t.call(a,b);case Node.DOCUMENT_FRAGMENT_NODE:return C.call(a,b);default:return n.call(a,b)}}var c=\"undefined\"===typeof HTMLTemplateElement,d=!(document.createDocumentFragment().cloneNode()instanceof DocumentFragment),e=!1;/Trident/.test(navigator.userAgent)&&function(){function a(a,b){if(a instanceof DocumentFragment)for(var d;d=a.firstChild;)c.call(this,d,b);else c.call(this,\na,b);return a}e=!0;var b=Node.prototype.cloneNode;Node.prototype.cloneNode=function(a){a=b.call(this,a);this instanceof DocumentFragment&&(a.__proto__=DocumentFragment.prototype);return a};DocumentFragment.prototype.querySelectorAll=HTMLElement.prototype.querySelectorAll;DocumentFragment.prototype.querySelector=HTMLElement.prototype.querySelector;Object.defineProperties(DocumentFragment.prototype,{nodeType:{get:function(){return Node.DOCUMENT_FRAGMENT_NODE},configurable:!0},localName:{get:function(){},\nconfigurable:!0},nodeName:{get:function(){return\"#document-fragment\"},configurable:!0}});var c=Node.prototype.insertBefore;Node.prototype.insertBefore=a;var d=Node.prototype.appendChild;Node.prototype.appendChild=function(b){b instanceof DocumentFragment?a.call(this,b,null):d.call(this,b);return b};var f=Node.prototype.removeChild,h=Node.prototype.replaceChild;Node.prototype.replaceChild=function(b,c){b instanceof DocumentFragment?(a.call(this,b,c),f.call(this,c)):h.call(this,b,c);return c};Document.prototype.createDocumentFragment=\nfunction(){var a=this.createElement(\"df\");a.__proto__=DocumentFragment.prototype;return a};var g=Document.prototype.importNode;Document.prototype.importNode=function(a,b){b=g.call(this,a,b||!1);a instanceof DocumentFragment&&(b.__proto__=DocumentFragment.prototype);return b}}();var f=Node.prototype.cloneNode,h=Document.prototype.createElement,g=Document.prototype.importNode,k=Node.prototype.removeChild,l=Node.prototype.appendChild,m=Node.prototype.replaceChild,n=Element.prototype.querySelectorAll,\nt=Document.prototype.querySelectorAll,C=DocumentFragment.prototype.querySelectorAll,eb=function(){if(!c){var a=document.createElement(\"template\"),b=document.createElement(\"template\");b.content.appendChild(document.createElement(\"div\"));a.content.appendChild(b);a=a.cloneNode(!0);return 0===a.content.childNodes.length||0===a.content.firstChild.content.childNodes.length||d}}();if(c){var J=document.implementation.createHTMLDocument(\"template\"),Ca=!0,Da=document.createElement(\"style\");Da.textContent=\"template{display:none;}\";\nvar Ea=document.head;Ea.insertBefore(Da,Ea.firstElementChild);a.prototype=Object.create(HTMLElement.prototype);var x=!document.createElement(\"div\").hasOwnProperty(\"innerHTML\");a.D=function(b){if(!b.content){b.content=J.createDocumentFragment();for(var c;c=b.firstChild;)l.call(b.content,c);if(x)b.__proto__=a.prototype;else if(b.cloneNode=function(b){return a.ca(this,b)},Ca)try{na(b),aa(b)}catch(Mg){Ca=!1}a.J(b.content)}};var na=function(b){Object.defineProperty(b,\"innerHTML\",{get:function(){for(var a=\n\"\",b=this.content.firstChild;b;b=b.nextSibling)a+=b.outerHTML||b.data.replace(oa,U);return a},set:function(b){J.body.innerHTML=b;for(a.J(J);this.content.firstChild;)k.call(this.content,this.content.firstChild);for(;J.body.firstChild;)l.call(this.content,J.body.firstChild)},configurable:!0})},aa=function(a){Object.defineProperty(a,\"outerHTML\",{get:function(){return\"<template>\"+this.innerHTML+\"</template>\"},set:function(a){if(this.parentNode){J.body.innerHTML=a;for(a=this.ownerDocument.createDocumentFragment();J.body.firstChild;)l.call(a,\nJ.body.firstChild);m.call(this.parentNode,a,this)}else throw Error(\"Failed to set the 'outerHTML' property on 'Element': This element has no parent node.\");},configurable:!0})};na(a.prototype);aa(a.prototype);a.J=function(c){c=b(c,\"template\");for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)a.D(f)};document.addEventListener(\"DOMContentLoaded\",function(){a.J(document)});Document.prototype.createElement=function(){var b=h.apply(this,arguments);\"template\"===b.localName&&a.D(b);return b};var oa=/[&\\u00A0<>]/g,\nU=function(a){switch(a){case \"&\":return\"&amp;\";case \"<\":return\"&lt;\";case \">\":return\"&gt;\";case \"\\u00a0\":return\"&nbsp;\"}}}if(c||eb){a.ca=function(a,b){var c=f.call(a,!1);this.D&&this.D(c);b&&(l.call(c.content,f.call(a.content,!0)),fb(c.content,a.content));return c};var fb=function(c,d){if(d.querySelectorAll&&(d=b(d,\"template\"),0!==d.length)){c=b(c,\"template\");for(var e=0,f=c.length,h,g;e<f;e++)g=d[e],h=c[e],a&&a.D&&a.D(g),m.call(h.parentNode,pa.call(g,!0),h)}},pa=Node.prototype.cloneNode=function(b){if(!e&&\nd&&this instanceof DocumentFragment)if(b)var c=qa.call(this.ownerDocument,this,!0);else return this.ownerDocument.createDocumentFragment();else c=this.nodeType===Node.ELEMENT_NODE&&\"template\"===this.localName?a.ca(this,b):f.call(this,b);b&&fb(c,this);return c},qa=Document.prototype.importNode=function(c,d){d=d||!1;if(\"template\"===c.localName)return a.ca(c,d);var e=g.call(this,c,d);if(d){fb(e,c);c=b(e,'script:not([type]),script[type=\"application/javascript\"],script[type=\"text/javascript\"]');for(var f,\nk=0;k<c.length;k++){f=c[k];d=h.call(document,\"script\");d.textContent=f.textContent;for(var l=f.attributes,qa=0,pa;qa<l.length;qa++)pa=l[qa],d.setAttribute(pa.name,pa.value);m.call(f.parentNode,d,f)}}return e}}c&&(window.HTMLTemplateElement=a)})();var ka=Array.isArray?Array.isArray:function(a){return\"[object Array]\"===Object.prototype.toString.call(a)};var la=0,ma,ra=\"undefined\"!==typeof window?window:void 0,sa=ra||{},ta=sa.MutationObserver||sa.WebKitMutationObserver,ua=\"undefined\"!==typeof Uint8ClampedArray&&\"undefined\"!==typeof importScripts&&\"undefined\"!==typeof MessageChannel;function va(){return\"undefined\"!==typeof ma?function(){ma(wa)}:xa()}function ya(){var a=0,b=new ta(wa),c=document.createTextNode(\"\");b.observe(c,{characterData:!0});return function(){c.data=a=++a%2}}\nfunction za(){var a=new MessageChannel;a.port1.onmessage=wa;return function(){return a.port2.postMessage(0)}}function xa(){var a=setTimeout;return function(){return a(wa,1)}}var Aa=Array(1E3);function wa(){for(var a=0;a<la;a+=2)(0,Aa[a])(Aa[a+1]),Aa[a]=void 0,Aa[a+1]=void 0;la=0}var Ba,Fa;\nif(\"undefined\"===typeof self&&\"undefined\"!==typeof process&&\"[object process]\"==={}.toString.call(process))Fa=function(){return process.ib(wa)};else{var Ga;if(ta)Ga=ya();else{var Ha;if(ua)Ha=za();else{var Ia;if(void 0===ra&&\"function\"===typeof require)try{var Ja=require(\"vertx\");ma=Ja.kb||Ja.jb;Ia=va()}catch(a){Ia=xa()}else Ia=xa();Ha=Ia}Ga=Ha}Fa=Ga}Ba=Fa;function Ka(a,b){Aa[la]=a;Aa[la+1]=b;la+=2;2===la&&Ba()};function La(a,b){var c=this,d=new this.constructor(Ma);void 0===d[Na]&&Oa(d);var e=c.g;if(e){var f=arguments[e-1];Ka(function(){return Pa(e,d,f,c.f)})}else Qa(c,d,a,b);return d};function Ra(a){if(a&&\"object\"===typeof a&&a.constructor===this)return a;var b=new this(Ma);Sa(b,a);return b};var Na=Math.random().toString(36).substring(16);function Ma(){}var Ua=new Ta;function Va(a){try{return a.then}catch(b){return Ua.error=b,Ua}}function Wa(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function Xa(a,b,c){Ka(function(a){var d=!1,f=Wa(c,b,function(c){d||(d=!0,b!==c?Sa(a,c):r(a,c))},function(b){d||(d=!0,u(a,b))});!d&&f&&(d=!0,u(a,f))},a)}function Ya(a,b){1===b.g?r(a,b.f):2===b.g?u(a,b.f):Qa(b,void 0,function(b){return Sa(a,b)},function(b){return u(a,b)})}\nfunction Za(a,b,c){b.constructor===a.constructor&&c===La&&b.constructor.resolve===Ra?Ya(a,b):c===Ua?(u(a,Ua.error),Ua.error=null):void 0===c?r(a,b):\"function\"===typeof c?Xa(a,b,c):r(a,b)}function Sa(a,b){if(a===b)u(a,new TypeError(\"You cannot resolve a promise with itself\"));else{var c=typeof b;null===b||\"object\"!==c&&\"function\"!==c?r(a,b):Za(a,b,Va(b))}}function $a(a){a.na&&a.na(a.f);ab(a)}function r(a,b){void 0===a.g&&(a.f=b,a.g=1,0!==a.I.length&&Ka(ab,a))}\nfunction u(a,b){void 0===a.g&&(a.g=2,a.f=b,Ka($a,a))}function Qa(a,b,c,d){var e=a.I,f=e.length;a.na=null;e[f]=b;e[f+1]=c;e[f+2]=d;0===f&&a.g&&Ka(ab,a)}function ab(a){var b=a.I,c=a.g;if(0!==b.length){for(var d,e,f=a.f,h=0;h<b.length;h+=3)d=b[h],e=b[h+c],d?Pa(c,d,e,f):e(f);a.I.length=0}}function Ta(){this.error=null}var bb=new Ta;\nfunction Pa(a,b,c,d){var e=\"function\"===typeof c;if(e){try{var f=c(d)}catch(l){bb.error=l,f=bb}if(f===bb){var h=!0;var g=f.error;f.error=null}else var k=!0;if(b===f){u(b,new TypeError(\"A promises callback cannot return that same promise.\"));return}}else f=d,k=!0;void 0===b.g&&(e&&k?Sa(b,f):h?u(b,g):1===a?r(b,f):2===a&&u(b,f))}function cb(a,b){try{b(function(b){Sa(a,b)},function(b){u(a,b)})}catch(c){u(a,c)}}var db=0;function Oa(a){a[Na]=db++;a.g=void 0;a.f=void 0;a.I=[]};function gb(a,b){this.Ea=a;this.A=new a(Ma);this.A[Na]||Oa(this.A);if(ka(b))if(this.S=this.length=b.length,this.f=Array(this.length),0===this.length)r(this.A,this.f);else{this.length=this.length||0;for(a=0;void 0===this.g&&a<b.length;a++)hb(this,b[a],a);0===this.S&&r(this.A,this.f)}else u(this.A,Error(\"Array Methods must be provided an Array\"))}\nfunction hb(a,b,c){var d=a.Ea,e=d.resolve;e===Ra?(e=Va(b),e===La&&void 0!==b.g?ib(a,b.g,c,b.f):\"function\"!==typeof e?(a.S--,a.f[c]=b):d===v?(d=new d(Ma),Za(d,b,e),jb(a,d,c)):jb(a,new d(function(a){return a(b)}),c)):jb(a,e(b),c)}function ib(a,b,c,d){var e=a.A;void 0===e.g&&(a.S--,2===b?u(e,d):a.f[c]=d);0===a.S&&r(e,a.f)}function jb(a,b,c){Qa(b,void 0,function(b){return ib(a,1,c,b)},function(b){return ib(a,2,c,b)})};function kb(a){return(new gb(this,a)).A};function lb(a){var b=this;return ka(a)?new b(function(c,d){for(var e=a.length,f=0;f<e;f++)b.resolve(a[f]).then(c,d)}):new b(function(a,b){return b(new TypeError(\"You must pass an array to race.\"))})};function mb(a){var b=new this(Ma);u(b,a);return b};function v(a){this[Na]=db++;this.f=this.g=void 0;this.I=[];if(Ma!==a){if(\"function\"!==typeof a)throw new TypeError(\"You must pass a resolver function as the first argument to the promise constructor\");if(this instanceof v)cb(this,a);else throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\");}}v.prototype={constructor:v,then:La,a:function(a){return this.then(null,a)}};/*\n\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\nwindow.Promise||(window.Promise=v,v.prototype[\"catch\"]=v.prototype.a,v.prototype.then=v.prototype.then,v.all=kb,v.race=lb,v.resolve=Ra,v.reject=mb);(function(a){function b(a,b){if(\"function\"===typeof window.CustomEvent)return new CustomEvent(a,b);var c=document.createEvent(\"CustomEvent\");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c}function c(a){if(C)return a.ownerDocument!==document?a.ownerDocument:null;var b=a.__importDoc;if(!b&&a.parentNode){b=a.parentNode;if(\"function\"===typeof b.closest)b=b.closest(\"link[rel=import]\");else for(;!g(b)&&(b=b.parentNode););a.__importDoc=b}return b}function d(a){var b=m(document,\"link[rel=import]:not([import-dependency])\"),\nc=b.length;c?n(b,function(b){return h(b,function(){0===--c&&a()})}):a()}function e(a){function b(){\"loading\"!==document.readyState&&document.body&&(document.removeEventListener(\"readystatechange\",b),a())}document.addEventListener(\"readystatechange\",b);b()}function f(a){e(function(){return d(function(){return a&&a()})})}function h(a,b){if(a.__loaded)b&&b();else if(\"script\"===a.localName&&!a.src||\"style\"===a.localName&&!a.firstChild)a.__loaded=!0,b&&b();else{var c=function(d){a.removeEventListener(d.type,\nc);a.__loaded=!0;b&&b()};a.addEventListener(\"load\",c);aa&&\"style\"===a.localName||a.addEventListener(\"error\",c)}}function g(a){return a.nodeType===Node.ELEMENT_NODE&&\"link\"===a.localName&&\"import\"===a.rel}function k(){var a=this;this.a={};this.b=0;this.c=new MutationObserver(function(b){return a.Ra(b)});this.c.observe(document.head,{childList:!0,subtree:!0});this.loadImports(document)}function l(a){n(m(a,\"template\"),function(a){n(m(a.content,'script:not([type]),script[type=\"application/javascript\"],script[type=\"text/javascript\"]'),\nfunction(a){var b=document.createElement(\"script\");n(a.attributes,function(a){return b.setAttribute(a.name,a.value)});b.textContent=a.textContent;a.parentNode.replaceChild(b,a)});l(a.content)})}function m(a,b){return a.childNodes.length?a.querySelectorAll(b):eb}function n(a,b,c){var d=a?a.length:0,e=c?-1:1;for(c=c?d-1:0;c<d&&0<=c;c+=e)b(a[c],c)}var t=document.createElement(\"link\"),C=\"import\"in t,eb=t.querySelectorAll(\"*\"),J=null;!1===\"currentScript\"in document&&Object.defineProperty(document,\"currentScript\",\n{get:function(){return J||(\"complete\"!==document.readyState?document.scripts[document.scripts.length-1]:null)},configurable:!0});var Ca=/(url\\()([^)]*)(\\))/g,Da=/(@import[\\s]+(?!url\\())([^;]*)(;)/g,Ea=/(<link[^>]*)(rel=['|\"]?stylesheet['|\"]?[^>]*>)/g,x={La:function(a,b){a.href&&a.setAttribute(\"href\",x.Y(a.getAttribute(\"href\"),b));a.src&&a.setAttribute(\"src\",x.Y(a.getAttribute(\"src\"),b));if(\"style\"===a.localName){var c=x.ta(a.textContent,b,Ca);a.textContent=x.ta(c,b,Da)}},ta:function(a,b,c){return a.replace(c,\nfunction(a,c,d,e){a=d.replace(/[\"']/g,\"\");b&&(a=x.Y(a,b));return c+\"'\"+a+\"'\"+e})},Y:function(a,b){if(void 0===x.ba){x.ba=!1;try{var c=new URL(\"b\",\"http://a\");c.pathname=\"c%20d\";x.ba=\"http://a/c%20d\"===c.href}catch(Lg){}}if(x.ba)return(new URL(a,b)).href;c=x.Ba;c||(c=document.implementation.createHTMLDocument(\"temp\"),x.Ba=c,c.ma=c.createElement(\"base\"),c.head.appendChild(c.ma),c.la=c.createElement(\"a\"));c.ma.href=b;c.la.href=a;return c.la.href||a}},na={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=\na.split(\",\");var d=a[1];d=-1<a[0].indexOf(\";base64\")?atob(d):decodeURIComponent(d);b(d)}else{var e=new XMLHttpRequest;e.open(\"GET\",a,na.async);e.onload=function(){var a=e.responseURL||e.getResponseHeader(\"Location\");a&&0===a.indexOf(\"/\")&&(a=(location.origin||location.protocol+\"//\"+location.host)+a);var d=e.response||e.responseText;304===e.status||0===e.status||200<=e.status&&300>e.status?b(d,a):c(d)};e.send()}else c(\"error: href must be specified\")}},aa=/Trident/.test(navigator.userAgent)||/Edge\\/\\d./i.test(navigator.userAgent);\nk.prototype.loadImports=function(a){var b=this;a=m(a,\"link[rel=import]\");n(a,function(a){return b.s(a)})};k.prototype.s=function(a){var b=this,c=a.href;if(void 0!==this.a[c]){var d=this.a[c];d&&d.__loaded&&(a.__import=d,this.h(a))}else this.b++,this.a[c]=\"pending\",na.load(c,function(a,d){a=b.Sa(a,d||c);b.a[c]=a;b.b--;b.loadImports(a);b.L()},function(){b.a[c]=null;b.b--;b.L()})};k.prototype.Sa=function(a,b){if(!a)return document.createDocumentFragment();aa&&(a=a.replace(Ea,function(a,b,c){return-1===\na.indexOf(\"type=\")?b+\" type=import-disable \"+c:a}));var c=document.createElement(\"template\");c.innerHTML=a;if(c.content)a=c.content,l(a);else for(a=document.createDocumentFragment();c.firstChild;)a.appendChild(c.firstChild);if(c=a.querySelector(\"base\"))b=x.Y(c.getAttribute(\"href\"),b),c.removeAttribute(\"href\");c=m(a,'link[rel=import],link[rel=stylesheet][href][type=import-disable],style:not([type]),link[rel=stylesheet][href]:not([type]),script:not([type]),script[type=\"application/javascript\"],script[type=\"text/javascript\"]');\nvar d=0;n(c,function(a){h(a);x.La(a,b);a.setAttribute(\"import-dependency\",\"\");\"script\"===a.localName&&!a.src&&a.textContent&&(a.setAttribute(\"src\",\"data:text/javascript;charset=utf-8,\"+encodeURIComponent(a.textContent+(\"\\n//# sourceURL=\"+b+(d?\"-\"+d:\"\")+\".js\\n\"))),a.textContent=\"\",d++)});return a};k.prototype.L=function(){var a=this;if(!this.b){this.c.disconnect();this.flatten(document);var b=!1,c=!1,d=function(){c&&b&&(a.loadImports(document),a.b||(a.c.observe(document.head,{childList:!0,subtree:!0}),\na.Pa()))};this.Ua(function(){c=!0;d()});this.Ta(function(){b=!0;d()})}};k.prototype.flatten=function(a){var b=this;a=m(a,\"link[rel=import]\");n(a,function(a){var c=b.a[a.href];(a.__import=c)&&c.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(b.a[a.href]=a,a.readyState=\"loading\",a.__import=a,b.flatten(c),a.appendChild(c))})};k.prototype.Ta=function(a){function b(e){if(e<d){var f=c[e],g=document.createElement(\"script\");f.removeAttribute(\"import-dependency\");n(f.attributes,function(a){return g.setAttribute(a.name,\na.value)});J=g;f.parentNode.replaceChild(g,f);h(g,function(){J=null;b(e+1)})}else a()}var c=m(document,\"script[import-dependency]\"),d=c.length;b(0)};k.prototype.Ua=function(a){var b=m(document,\"style[import-dependency],link[rel=stylesheet][import-dependency]\"),d=b.length;if(d){var e=aa&&!!document.querySelector(\"link[rel=stylesheet][href][type=import-disable]\");n(b,function(b){h(b,function(){b.removeAttribute(\"import-dependency\");0===--d&&a()});if(e&&b.parentNode!==document.head){var f=document.createElement(b.localName);\nf.__appliedElement=b;f.setAttribute(\"type\",\"import-placeholder\");b.parentNode.insertBefore(f,b.nextSibling);for(f=c(b);f&&c(f);)f=c(f);f.parentNode!==document.head&&(f=null);document.head.insertBefore(b,f);b.removeAttribute(\"type\")}})}else a()};k.prototype.Pa=function(){var a=this,b=m(document,\"link[rel=import]\");n(b,function(b){return a.h(b)},!0)};k.prototype.h=function(a){a.__loaded||(a.__loaded=!0,a.import&&(a.import.readyState=\"complete\"),a.dispatchEvent(b(a.import?\"load\":\"error\",{bubbles:!1,\ncancelable:!1,detail:void 0})))};k.prototype.Ra=function(a){var b=this;n(a,function(a){return n(a.addedNodes,function(a){a&&a.nodeType===Node.ELEMENT_NODE&&(g(a)?b.s(a):b.loadImports(a))})})};var oa=null;if(C)t=m(document,\"link[rel=import]\"),n(t,function(a){a.import&&\"loading\"===a.import.readyState||(a.__loaded=!0)}),t=function(a){a=a.target;g(a)&&(a.__loaded=!0)},document.addEventListener(\"load\",t,!0),document.addEventListener(\"error\",t,!0);else{var U=Object.getOwnPropertyDescriptor(Node.prototype,\n\"baseURI\");Object.defineProperty((!U||U.configurable?Node:Element).prototype,\"baseURI\",{get:function(){var a=g(this)?this:c(this);return a?a.href:U&&U.get?U.get.call(this):(document.querySelector(\"base\")||window.location).href},configurable:!0,enumerable:!0});Object.defineProperty(HTMLLinkElement.prototype,\"import\",{get:function(){return this.__import||null},configurable:!0,enumerable:!0});e(function(){oa=new k})}f(function(){return document.dispatchEvent(b(\"HTMLImportsLoaded\",{cancelable:!0,bubbles:!0,\ndetail:void 0}))});a.useNative=C;a.whenReady=f;a.importForElement=c;a.loadImports=function(a){oa&&oa.loadImports(a)}})(window.HTMLImports=window.HTMLImports||{});/*\n\n Copyright (c) 2014 The Polymer Project Authors. All rights reserved.\n This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\n The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\n The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\n Code distributed by Google as part of the polymer project is also\n subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\nwindow.WebComponents=window.WebComponents||{flags:{}};var nb=document.querySelector('script[src*=\"webcomponents-lite.js\"]'),ob=/wc-(.+)/,w={};if(!w.noOpts){location.search.slice(1).split(\"&\").forEach(function(a){a=a.split(\"=\");var b;a[0]&&(b=a[0].match(ob))&&(w[b[1]]=a[1]||!0)});if(nb)for(var pb=0,qb;qb=nb.attributes[pb];pb++)\"src\"!==qb.name&&(w[qb.name]=qb.value||!0);if(w.log&&w.log.split){var rb=w.log.split(\",\");w.log={};rb.forEach(function(a){w.log[a]=!0})}else w.log={}}\nwindow.WebComponents.flags=w;var sb=w.shadydom;sb&&(window.ShadyDOM=window.ShadyDOM||{},window.ShadyDOM.force=sb);var tb=w.register||w.ce;tb&&window.customElements&&(window.customElements.forcePolyfill=tb);/*\n\nCopyright (c) 2016 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\nvar y=window.ShadyDOM||{};y.Na=!(!Element.prototype.attachShadow||!Node.prototype.getRootNode);var ub=Object.getOwnPropertyDescriptor(Node.prototype,\"firstChild\");y.M=!!(ub&&ub.configurable&&ub.get);y.sa=y.force||!y.Na;function vb(a){return a.__shady&&void 0!==a.__shady.firstChild}function z(a){return\"ShadyRoot\"===a.ya}function wb(a){a=a.getRootNode();if(z(a))return a}var xb=Element.prototype,yb=xb.matches||xb.matchesSelector||xb.mozMatchesSelector||xb.msMatchesSelector||xb.oMatchesSelector||xb.webkitMatchesSelector;\nfunction zb(a,b){if(a&&b)for(var c=Object.getOwnPropertyNames(b),d=0,e;d<c.length&&(e=c[d]);d++){var f=Object.getOwnPropertyDescriptor(b,e);f&&Object.defineProperty(a,e,f)}}function Ab(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];for(d=0;d<c.length;d++)zb(a,c[d]);return a}function Bb(a,b){for(var c in b)a[c]=b[c]}var Cb=document.createTextNode(\"\"),Db=0,Eb=[];(new MutationObserver(function(){for(;Eb.length;)try{Eb.shift()()}catch(a){throw Cb.textContent=Db++,a;}})).observe(Cb,{characterData:!0});\nfunction Fb(a){Eb.push(a);Cb.textContent=Db++}var Gb=!!document.contains;function Hb(a,b){for(;b;){if(b==a)return!0;b=b.parentNode}return!1};var Ib=[],Jb;function Kb(a){Jb||(Jb=!0,Fb(Lb));Ib.push(a)}function Lb(){Jb=!1;for(var a=!!Ib.length;Ib.length;)Ib.shift()();return a}Lb.list=Ib;function Mb(){this.a=!1;this.addedNodes=[];this.removedNodes=[];this.V=new Set}function Nb(a){a.a||(a.a=!0,Fb(function(){Ob(a)}))}function Ob(a){if(a.a){a.a=!1;var b=a.takeRecords();b.length&&a.V.forEach(function(a){a(b)})}}Mb.prototype.takeRecords=function(){if(this.addedNodes.length||this.removedNodes.length){var a=[{addedNodes:this.addedNodes,removedNodes:this.removedNodes}];this.addedNodes=[];this.removedNodes=[];return a}return[]};\nfunction Pb(a,b){a.__shady=a.__shady||{};a.__shady.N||(a.__shady.N=new Mb);a.__shady.N.V.add(b);var c=a.__shady.N;return{Ca:b,C:c,Ga:a,takeRecords:function(){return c.takeRecords()}}}function Qb(a){var b=a&&a.C;b&&(b.V.delete(a.Ca),b.V.size||(a.Ga.__shady.N=null))}\nfunction Rb(a,b){var c=b.getRootNode();return a.map(function(a){var b=c===a.target.getRootNode();if(b&&a.addedNodes){if(b=Array.from(a.addedNodes).filter(function(a){return c===a.getRootNode()}),b.length)return a=Object.create(a),Object.defineProperty(a,\"addedNodes\",{value:b,configurable:!0}),a}else if(b)return a}).filter(function(a){return a})};var A={},Sb=Element.prototype.insertBefore,Tb=Element.prototype.removeChild,Ub=Element.prototype.setAttribute,Vb=Element.prototype.removeAttribute,Wb=Element.prototype.cloneNode,Xb=Document.prototype.importNode,Yb=Element.prototype.addEventListener,Zb=Element.prototype.removeEventListener,$b=Window.prototype.addEventListener,ac=Window.prototype.removeEventListener,bc=Element.prototype.dispatchEvent,cc=Element.prototype.querySelector,dc=Element.prototype.querySelectorAll,ec=Node.prototype.contains||\nHTMLElement.prototype.contains;A.appendChild=Element.prototype.appendChild;A.insertBefore=Sb;A.removeChild=Tb;A.setAttribute=Ub;A.removeAttribute=Vb;A.cloneNode=Wb;A.importNode=Xb;A.addEventListener=Yb;A.removeEventListener=Zb;A.ab=$b;A.bb=ac;A.dispatchEvent=bc;A.querySelector=cc;A.querySelectorAll=dc;A.contains=ec;var fc=/[&\\u00A0\"]/g,gc=/[&\\u00A0<>]/g;function hc(a){switch(a){case \"&\":return\"&amp;\";case \"<\":return\"&lt;\";case \">\":return\"&gt;\";case '\"':return\"&quot;\";case \"\\u00a0\":return\"&nbsp;\"}}function ic(a){for(var b={},c=0;c<a.length;c++)b[a[c]]=!0;return b}var jc=ic(\"area base br col command embed hr img input keygen link meta param source track wbr\".split(\" \")),kc=ic(\"style script xmp iframe noembed noframes plaintext noscript\".split(\" \"));\nfunction lc(a,b){\"template\"===a.localName&&(a=a.content);for(var c=\"\",d=b?b(a):a.childNodes,e=0,f=d.length,h;e<f&&(h=d[e]);e++){a:{var g=h;var k=a;var l=b;switch(g.nodeType){case Node.ELEMENT_NODE:for(var m=g.localName,n=\"<\"+m,t=g.attributes,C=0;k=t[C];C++)n+=\" \"+k.name+'=\"'+k.value.replace(fc,hc)+'\"';n+=\">\";g=jc[m]?n:n+lc(g,l)+\"</\"+m+\">\";break a;case Node.TEXT_NODE:g=g.data;g=k&&kc[k.localName]?g:g.replace(gc,hc);break a;case Node.COMMENT_NODE:g=\"\\x3c!--\"+g.data+\"--\\x3e\";break a;default:throw window.console.error(g),\nError(\"not implemented\");}}c+=g}return c};var B={},D=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,!1),E=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT,null,!1);function mc(a){var b=[];D.currentNode=a;for(a=D.firstChild();a;)b.push(a),a=D.nextSibling();return b}B.parentNode=function(a){D.currentNode=a;return D.parentNode()};B.firstChild=function(a){D.currentNode=a;return D.firstChild()};B.lastChild=function(a){D.currentNode=a;return D.lastChild()};B.previousSibling=function(a){D.currentNode=a;return D.previousSibling()};\nB.nextSibling=function(a){D.currentNode=a;return D.nextSibling()};B.childNodes=mc;B.parentElement=function(a){E.currentNode=a;return E.parentNode()};B.firstElementChild=function(a){E.currentNode=a;return E.firstChild()};B.lastElementChild=function(a){E.currentNode=a;return E.lastChild()};B.previousElementSibling=function(a){E.currentNode=a;return E.previousSibling()};B.nextElementSibling=function(a){E.currentNode=a;return E.nextSibling()};\nB.children=function(a){var b=[];E.currentNode=a;for(a=E.firstChild();a;)b.push(a),a=E.nextSibling();return b};B.innerHTML=function(a){return lc(a,function(a){return mc(a)})};B.textContent=function(a){switch(a.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:a=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1);for(var b=\"\",c;c=a.nextNode();)b+=c.nodeValue;return b;default:return a.nodeValue}};var nc=Object.getOwnPropertyDescriptor(Element.prototype,\"innerHTML\")||Object.getOwnPropertyDescriptor(HTMLElement.prototype,\"innerHTML\"),oc=document.implementation.createHTMLDocument(\"inert\"),pc=Object.getOwnPropertyDescriptor(Document.prototype,\"activeElement\"),qc={parentElement:{get:function(){var a=this.__shady&&this.__shady.parentNode;a&&a.nodeType!==Node.ELEMENT_NODE&&(a=null);return void 0!==a?a:B.parentElement(this)},configurable:!0},parentNode:{get:function(){var a=this.__shady&&this.__shady.parentNode;\nreturn void 0!==a?a:B.parentNode(this)},configurable:!0},nextSibling:{get:function(){var a=this.__shady&&this.__shady.nextSibling;return void 0!==a?a:B.nextSibling(this)},configurable:!0},previousSibling:{get:function(){var a=this.__shady&&this.__shady.previousSibling;return void 0!==a?a:B.previousSibling(this)},configurable:!0},className:{get:function(){return this.getAttribute(\"class\")||\"\"},set:function(a){this.setAttribute(\"class\",a)},configurable:!0},nextElementSibling:{get:function(){if(this.__shady&&\nvoid 0!==this.__shady.nextSibling){for(var a=this.nextSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return B.nextElementSibling(this)},configurable:!0},previousElementSibling:{get:function(){if(this.__shady&&void 0!==this.__shady.previousSibling){for(var a=this.previousSibling;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return B.previousElementSibling(this)},configurable:!0}},rc={childNodes:{get:function(){if(vb(this)){if(!this.__shady.childNodes){this.__shady.childNodes=\n[];for(var a=this.firstChild;a;a=a.nextSibling)this.__shady.childNodes.push(a)}var b=this.__shady.childNodes}else b=B.childNodes(this);b.item=function(a){return b[a]};return b},configurable:!0},childElementCount:{get:function(){return this.children.length},configurable:!0},firstChild:{get:function(){var a=this.__shady&&this.__shady.firstChild;return void 0!==a?a:B.firstChild(this)},configurable:!0},lastChild:{get:function(){var a=this.__shady&&this.__shady.lastChild;return void 0!==a?a:B.lastChild(this)},\nconfigurable:!0},textContent:{get:function(){if(vb(this)){for(var a=[],b=0,c=this.childNodes,d;d=c[b];b++)d.nodeType!==Node.COMMENT_NODE&&a.push(d.textContent);return a.join(\"\")}return B.textContent(this)},set:function(a){if(\"undefined\"===typeof a||null===a)a=\"\";switch(this.nodeType){case Node.ELEMENT_NODE:case Node.DOCUMENT_FRAGMENT_NODE:for(;this.firstChild;)this.removeChild(this.firstChild);(0<a.length||this.nodeType===Node.ELEMENT_NODE)&&this.appendChild(document.createTextNode(a));break;default:this.nodeValue=\na}},configurable:!0},firstElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.firstChild){for(var a=this.firstChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.nextSibling;return a}return B.firstElementChild(this)},configurable:!0},lastElementChild:{get:function(){if(this.__shady&&void 0!==this.__shady.lastChild){for(var a=this.lastChild;a&&a.nodeType!==Node.ELEMENT_NODE;)a=a.previousSibling;return a}return B.lastElementChild(this)},configurable:!0},children:{get:function(){var a=vb(this)?\nArray.prototype.filter.call(this.childNodes,function(a){return a.nodeType===Node.ELEMENT_NODE}):B.children(this);a.item=function(b){return a[b]};return a},configurable:!0},innerHTML:{get:function(){var a=\"template\"===this.localName?this.content:this;return vb(this)?lc(a):B.innerHTML(a)},set:function(a){for(var b=\"template\"===this.localName?this.content:this;b.firstChild;)b.removeChild(b.firstChild);var c=this.localName;c&&\"template\"!==c||(c=\"div\");c=oc.createElement(c);for(nc&&nc.set?nc.set.call(c,\na):c.innerHTML=a;c.firstChild;)b.appendChild(c.firstChild)},configurable:!0}},sc={shadowRoot:{get:function(){return this.__shady&&this.__shady.Va||null},configurable:!0}},tc={activeElement:{get:function(){var a=pc&&pc.get?pc.get.call(document):y.M?void 0:document.activeElement;if(a&&a.nodeType){var b=!!z(this);if(this===document||b&&this.host!==a&&A.contains.call(this.host,a)){for(b=wb(a);b&&b!==this;)a=b.host,b=wb(a);a=this===document?b?null:a:b===this?a:null}else a=null}else a=null;return a},set:function(){},\nconfigurable:!0}};function F(a,b,c){for(var d in b){var e=Object.getOwnPropertyDescriptor(a,d);e&&e.configurable||!e&&c?Object.defineProperty(a,d,b[d]):c&&console.warn(\"Could not define\",d,\"on\",a)}}function G(a){F(a,qc);F(a,rc);F(a,tc)}var uc=y.M?function(){}:function(a){a.__shady&&a.__shady.za||(a.__shady=a.__shady||{},a.__shady.za=!0,F(a,qc,!0))},vc=y.M?function(){}:function(a){a.__shady&&a.__shady.xa||(a.__shady=a.__shady||{},a.__shady.xa=!0,F(a,rc,!0),F(a,sc,!0))};function wc(a,b,c){uc(a);c=c||null;a.__shady=a.__shady||{};b.__shady=b.__shady||{};c&&(c.__shady=c.__shady||{});a.__shady.previousSibling=c?c.__shady.previousSibling:b.lastChild;var d=a.__shady.previousSibling;d&&d.__shady&&(d.__shady.nextSibling=a);(d=a.__shady.nextSibling=c)&&d.__shady&&(d.__shady.previousSibling=a);a.__shady.parentNode=b;c?c===b.__shady.firstChild&&(b.__shady.firstChild=a):(b.__shady.lastChild=a,b.__shady.firstChild||(b.__shady.firstChild=a));b.__shady.childNodes=null}\nfunction xc(a){if(!a.__shady||void 0===a.__shady.firstChild){a.__shady=a.__shady||{};a.__shady.firstChild=B.firstChild(a);a.__shady.lastChild=B.lastChild(a);vc(a);for(var b=a.__shady.childNodes=B.childNodes(a),c=0,d;c<b.length&&(d=b[c]);c++)d.__shady=d.__shady||{},d.__shady.parentNode=a,d.__shady.nextSibling=b[c+1]||null,d.__shady.previousSibling=b[c-1]||null,uc(d)}};function yc(a,b,c){if(b===a)throw Error(\"Failed to execute 'appendChild' on 'Node': The new child element contains the parent.\");if(c){var d=c.__shady&&c.__shady.parentNode;if(void 0!==d&&d!==a||void 0===d&&B.parentNode(c)!==a)throw Error(\"Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.\");}if(c===b)return b;b.parentNode&&zc(b.parentNode,b);d=wb(a);var e;if(e=d)a:{if(!b.__noInsertionPoint){var f;\"slot\"===b.localName?f=[b]:\nb.querySelectorAll&&(f=b.querySelectorAll(\"slot\"));if(f&&f.length){e=f;break a}}e=void 0}(f=e)&&d.H.push.apply(d.H,[].concat(f instanceof Array?f:ja(ia(f))));d&&(\"slot\"===a.localName||f)&&Ac(d);if(vb(a)){d=c;vc(a);a.__shady=a.__shady||{};void 0!==a.__shady.firstChild&&(a.__shady.childNodes=null);if(b.nodeType===Node.DOCUMENT_FRAGMENT_NODE){f=b.childNodes;for(e=0;e<f.length;e++)wc(f[e],a,d);b.__shady=b.__shady||{};d=void 0!==b.__shady.firstChild?null:void 0;b.__shady.firstChild=b.__shady.lastChild=\nd;b.__shady.childNodes=d}else wc(b,a,d);if(Bc(a)){Ac(a.__shady.root);var h=!0}else a.__shady.root&&(h=!0)}h||(h=z(a)?a.host:a,c?(c=Cc(c),A.insertBefore.call(h,b,c)):A.appendChild.call(h,b));Dc(a,b);return b}\nfunction zc(a,b){if(b.parentNode!==a)throw Error(\"The node to be removed is not a child of this node: \"+b);var c=wb(b);if(vb(a)){b.__shady=b.__shady||{};a.__shady=a.__shady||{};b===a.__shady.firstChild&&(a.__shady.firstChild=b.__shady.nextSibling);b===a.__shady.lastChild&&(a.__shady.lastChild=b.__shady.previousSibling);var d=b.__shady.previousSibling,e=b.__shady.nextSibling;d&&(d.__shady=d.__shady||{},d.__shady.nextSibling=e);e&&(e.__shady=e.__shady||{},e.__shady.previousSibling=d);b.__shady.parentNode=\nb.__shady.previousSibling=b.__shady.nextSibling=void 0;void 0!==a.__shady.childNodes&&(a.__shady.childNodes=null);if(Bc(a)){Ac(a.__shady.root);var f=!0}}Ec(b);if(c){(d=a&&\"slot\"===a.localName)&&(f=!0);Fc(c);e=c.l;for(var h in e)for(var g=e[h],k=0;k<g.length;k++){var l=g[k];if(Hb(b,l)){g.splice(k,1);var m=c.o.indexOf(l);0<=m&&c.o.splice(m,1);k--;if(m=l.__shady.K)for(l=0;l<m.length;l++){var n=m[l],t=B.parentNode(n);t&&A.removeChild.call(t,n)}m=!0}}(m||d)&&Ac(c)}f||(f=z(a)?a.host:a,(!a.__shady.root&&\n\"slot\"!==b.localName||f===B.parentNode(b))&&A.removeChild.call(f,b));Dc(a,null,b);return b}function Ec(a){if(a.__shady&&void 0!==a.__shady.ka)for(var b=a.childNodes,c=0,d=b.length,e;c<d&&(e=b[c]);c++)Ec(e);a.__shady&&(a.__shady.ka=void 0)}function Cc(a){var b=a;a&&\"slot\"===a.localName&&(b=(b=a.__shady&&a.__shady.K)&&b.length?b[0]:Cc(a.nextSibling));return b}function Bc(a){return(a=a&&a.__shady&&a.__shady.root)&&Gc(a)}\nfunction Hc(a,b){if(\"slot\"===b)a=a.parentNode,Bc(a)&&Ac(a.__shady.root);else if(\"slot\"===a.localName&&\"name\"===b&&(b=wb(a))){var c=a.Aa,d=Ic(a);if(d!==c){c=b.l[c];var e=c.indexOf(a);0<=e&&c.splice(e,1);c=b.l[d]||(b.l[d]=[]);c.push(a);1<c.length&&(b.l[d]=Jc(c))}Ac(b)}}function Dc(a,b,c){if(a=a.__shady&&a.__shady.N)b&&a.addedNodes.push(b),c&&a.removedNodes.push(c),Nb(a)}\nfunction Kc(a){if(a&&a.nodeType){a.__shady=a.__shady||{};var b=a.__shady.ka;void 0===b&&(b=z(a)?a:(b=a.parentNode)?Kc(b):a,A.contains.call(document.documentElement,a)&&(a.__shady.ka=b));return b}}function Lc(a,b,c){var d=[];Mc(a.childNodes,b,c,d);return d}function Mc(a,b,c,d){for(var e=0,f=a.length,h;e<f&&(h=a[e]);e++){var g;if(g=h.nodeType===Node.ELEMENT_NODE){g=h;var k=b,l=c,m=d,n=k(g);n&&m.push(g);l&&l(n)?g=n:(Mc(g.childNodes,k,l,m),g=void 0)}if(g)break}}var Nc=null;\nfunction Oc(a,b,c){Nc||(Nc=window.ShadyCSS&&window.ShadyCSS.ScopingShim);Nc&&\"class\"===b?Nc.setElementClass(a,c):(A.setAttribute.call(a,b,c),Hc(a,b))}function Pc(a,b){if(a.ownerDocument!==document)return A.importNode.call(document,a,b);var c=A.importNode.call(document,a,!1);if(b){a=a.childNodes;b=0;for(var d;b<a.length;b++)d=Pc(a[b],!0),c.appendChild(d)}return c};var Qc=\"__eventWrappers\"+Date.now(),Rc={blur:!0,focus:!0,focusin:!0,focusout:!0,click:!0,dblclick:!0,mousedown:!0,mouseenter:!0,mouseleave:!0,mousemove:!0,mouseout:!0,mouseover:!0,mouseup:!0,wheel:!0,beforeinput:!0,input:!0,keydown:!0,keyup:!0,compositionstart:!0,compositionupdate:!0,compositionend:!0,touchstart:!0,touchend:!0,touchmove:!0,touchcancel:!0,pointerover:!0,pointerenter:!0,pointerdown:!0,pointermove:!0,pointerup:!0,pointercancel:!0,pointerout:!0,pointerleave:!0,gotpointercapture:!0,lostpointercapture:!0,\ndragstart:!0,drag:!0,dragenter:!0,dragleave:!0,dragover:!0,drop:!0,dragend:!0,DOMActivate:!0,DOMFocusIn:!0,DOMFocusOut:!0,keypress:!0};function Sc(a,b){var c=[],d=a;for(a=a===window?window:a.getRootNode();d;)c.push(d),d=d.assignedSlot?d.assignedSlot:d.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&d.host&&(b||d!==a)?d.host:d.parentNode;c[c.length-1]===document&&c.push(window);return c}\nfunction Tc(a,b){if(!z)return a;a=Sc(a,!0);for(var c=0,d,e,f,h;c<b.length;c++)if(d=b[c],f=d===window?window:d.getRootNode(),f!==e&&(h=a.indexOf(f),e=f),!z(f)||-1<h)return d}\nvar Uc={get composed(){!1!==this.isTrusted&&void 0===this.Z&&(this.Z=Rc[this.type]);return this.Z||!1},composedPath:function(){this.b||(this.b=Sc(this.__target,this.composed));return this.b},get target(){return Tc(this.currentTarget,this.composedPath())},get relatedTarget(){if(!this.$)return null;this.c||(this.c=Sc(this.$,!0));return Tc(this.currentTarget,this.c)},stopPropagation:function(){Event.prototype.stopPropagation.call(this);this.a=!0},stopImmediatePropagation:function(){Event.prototype.stopImmediatePropagation.call(this);\nthis.a=this.h=!0}};function Vc(a){function b(b,d){b=new a(b,d);b.Z=d&&!!d.composed;return b}Bb(b,a);b.prototype=a.prototype;return b}var Wc={focus:!0,blur:!0};function Xc(a){return a.__target!==a.target||a.$!==a.relatedTarget}function Yc(a,b,c){if(c=b.__handlers&&b.__handlers[a.type]&&b.__handlers[a.type][c])for(var d=0,e;(e=c[d])&&(!Xc(a)||a.target!==a.relatedTarget)&&(e.call(b,a),!a.h);d++);}\nfunction Zc(a){var b=a.composedPath();Object.defineProperty(a,\"currentTarget\",{get:function(){return d},configurable:!0});for(var c=b.length-1;0<=c;c--){var d=b[c];Yc(a,d,\"capture\");if(a.a)return}Object.defineProperty(a,\"eventPhase\",{get:function(){return Event.AT_TARGET}});var e;for(c=0;c<b.length;c++){d=b[c];var f=d.__shady&&d.__shady.root;if(0===c||f&&f===e)if(Yc(a,d,\"bubble\"),d!==window&&(e=d.getRootNode()),a.a)break}}\nfunction $c(a,b,c,d,e,f){for(var h=0;h<a.length;h++){var g=a[h],k=g.type,l=g.capture,m=g.once,n=g.passive;if(b===g.node&&c===k&&d===l&&e===m&&f===n)return h}return-1}\nfunction ad(a,b,c){if(b){var d=typeof b;if(\"function\"===d||\"object\"===d)if(\"object\"!==d||b.handleEvent&&\"function\"===typeof b.handleEvent){if(c&&\"object\"===typeof c){var e=!!c.capture;var f=!!c.once;var h=!!c.passive}else e=!!c,h=f=!1;var g=c&&c.aa||this,k=b[Qc];if(k){if(-1<$c(k,g,a,e,f,h))return}else b[Qc]=[];k=function(e){f&&this.removeEventListener(a,b,c);e.__target||bd(e);if(g!==this){var h=Object.getOwnPropertyDescriptor(e,\"currentTarget\");Object.defineProperty(e,\"currentTarget\",{get:function(){return g},\nconfigurable:!0})}if(e.composed||-1<e.composedPath().indexOf(g))if(Xc(e)&&e.target===e.relatedTarget)e.eventPhase===Event.BUBBLING_PHASE&&e.stopImmediatePropagation();else if(e.eventPhase===Event.CAPTURING_PHASE||e.bubbles||e.target===g||g instanceof Window){var k=\"function\"===d?b.call(g,e):b.handleEvent&&b.handleEvent(e);g!==this&&(h?(Object.defineProperty(e,\"currentTarget\",h),h=null):delete e.currentTarget);return k}};b[Qc].push({node:this,type:a,capture:e,once:f,passive:h,cb:k});Wc[a]?(this.__handlers=\nthis.__handlers||{},this.__handlers[a]=this.__handlers[a]||{capture:[],bubble:[]},this.__handlers[a][e?\"capture\":\"bubble\"].push(k)):(this instanceof Window?A.ab:A.addEventListener).call(this,a,k,c)}}}\nfunction cd(a,b,c){if(b){if(c&&\"object\"===typeof c){var d=!!c.capture;var e=!!c.once;var f=!!c.passive}else d=!!c,f=e=!1;var h=c&&c.aa||this,g=void 0;var k=null;try{k=b[Qc]}catch(l){}k&&(e=$c(k,h,a,d,e,f),-1<e&&(g=k.splice(e,1)[0].cb,k.length||(b[Qc]=void 0)));(this instanceof Window?A.bb:A.removeEventListener).call(this,a,g||b,c);g&&Wc[a]&&this.__handlers&&this.__handlers[a]&&(a=this.__handlers[a][d?\"capture\":\"bubble\"],g=a.indexOf(g),-1<g&&a.splice(g,1))}}\nfunction dd(){for(var a in Wc)window.addEventListener(a,function(a){a.__target||(bd(a),Zc(a))},!0)}function bd(a){a.__target=a.target;a.$=a.relatedTarget;if(y.M){var b=Object.getPrototypeOf(a);if(!b.hasOwnProperty(\"__patchProto\")){var c=Object.create(b);c.fb=b;zb(c,Uc);b.__patchProto=c}a.__proto__=b.__patchProto}else zb(a,Uc)}var ed=Vc(window.Event),fd=Vc(window.CustomEvent),gd=Vc(window.MouseEvent);function hd(a,b){return{index:a,O:[],U:b}}\nfunction id(a,b,c,d){var e=0,f=0,h=0,g=0,k=Math.min(b-e,d-f);if(0==e&&0==f)a:{for(h=0;h<k;h++)if(a[h]!==c[h])break a;h=k}if(b==a.length&&d==c.length){g=a.length;for(var l=c.length,m=0;m<k-h&&jd(a[--g],c[--l]);)m++;g=m}e+=h;f+=h;b-=g;d-=g;if(0==b-e&&0==d-f)return[];if(e==b){for(b=hd(e,0);f<d;)b.O.push(c[f++]);return[b]}if(f==d)return[hd(e,b-e)];k=e;h=f;d=d-h+1;g=b-k+1;b=Array(d);for(l=0;l<d;l++)b[l]=Array(g),b[l][0]=l;for(l=0;l<g;l++)b[0][l]=l;for(l=1;l<d;l++)for(m=1;m<g;m++)if(a[k+m-1]===c[h+l-1])b[l][m]=\nb[l-1][m-1];else{var n=b[l-1][m]+1,t=b[l][m-1]+1;b[l][m]=n<t?n:t}k=b.length-1;h=b[0].length-1;d=b[k][h];for(a=[];0<k||0<h;)0==k?(a.push(2),h--):0==h?(a.push(3),k--):(g=b[k-1][h-1],l=b[k-1][h],m=b[k][h-1],n=l<m?l<g?l:g:m<g?m:g,n==g?(g==d?a.push(0):(a.push(1),d=g),k--,h--):n==l?(a.push(3),k--,d=l):(a.push(2),h--,d=m));a.reverse();b=void 0;k=[];for(h=0;h<a.length;h++)switch(a[h]){case 0:b&&(k.push(b),b=void 0);e++;f++;break;case 1:b||(b=hd(e,0));b.U++;e++;b.O.push(c[f]);f++;break;case 2:b||(b=hd(e,0));\nb.U++;e++;break;case 3:b||(b=hd(e,0)),b.O.push(c[f]),f++}b&&k.push(b);return k}function jd(a,b){return a===b};var kd={};function H(a,b,c){if(a!==kd)throw new TypeError(\"Illegal constructor\");a=document.createDocumentFragment();a.__proto__=H.prototype;a.ya=\"ShadyRoot\";xc(b);xc(a);a.host=b;a.Fa=c&&c.mode;b.__shady=b.__shady||{};b.__shady.root=a;b.__shady.Va=\"closed\"!==a.Fa?a:null;a.T=!1;a.o=[];a.l={};a.H=[];c=B.childNodes(b);for(var d=0,e=c.length;d<e;d++)A.removeChild.call(b,c[d]);return a}H.prototype=Object.create(DocumentFragment.prototype);function Ac(a){a.T||(a.T=!0,Kb(function(){return ld(a)}))}\nfunction ld(a){for(var b;a;){a.T&&(b=a);a:{var c=a;a=c.host.getRootNode();if(z(a))for(var d=c.host.childNodes,e=0;e<d.length;e++)if(c=d[e],\"slot\"==c.localName)break a;a=void 0}}b&&b._renderRoot()}\nH.prototype._renderRoot=function(){this.T=!1;Fc(this);for(var a=0,b;a<this.o.length;a++){b=this.o[a];var c=b.__shady.assignedNodes;b.__shady.assignedNodes=[];b.__shady.K=[];if(b.__shady.oa=c)for(var d=0;d<c.length;d++){var e=c[d];e.__shady.ga=e.__shady.assignedSlot;e.__shady.assignedSlot===b&&(e.__shady.assignedSlot=null)}}for(b=this.host.firstChild;b;b=b.nextSibling)md(this,b);for(a=0;a<this.o.length;a++){b=this.o[a];if(!b.__shady.assignedNodes.length)for(c=b.firstChild;c;c=c.nextSibling)md(this,\nc,b);c=b.parentNode;(c=c.__shady&&c.__shady.root)&&Gc(c)&&c._renderRoot();nd(this,b.__shady.K,b.__shady.assignedNodes);if(c=b.__shady.oa){for(d=0;d<c.length;d++)c[d].__shady.ga=null;b.__shady.oa=null;c.length>b.__shady.assignedNodes.length&&(b.__shady.ia=!0)}b.__shady.ia&&(b.__shady.ia=!1,od(this,b))}a=this.o;b=[];for(c=0;c<a.length;c++)d=a[c].parentNode,d.__shady&&d.__shady.root||!(0>b.indexOf(d))||b.push(d);for(a=0;a<b.length;a++){c=b[a];d=c===this?this.host:c;e=[];c=c.childNodes;for(var f=0;f<\nc.length;f++){var h=c[f];if(\"slot\"==h.localName){h=h.__shady.K;for(var g=0;g<h.length;g++)e.push(h[g])}else e.push(h)}c=void 0;f=B.childNodes(d);h=id(e,e.length,f,f.length);for(var k=g=0;g<h.length&&(c=h[g]);g++){for(var l=0,m;l<c.O.length&&(m=c.O[l]);l++)B.parentNode(m)===d&&A.removeChild.call(d,m),f.splice(c.index+k,1);k-=c.U}for(k=0;k<h.length&&(c=h[k]);k++)for(g=f[c.index],l=c.index;l<c.index+c.U;l++)m=e[l],A.insertBefore.call(d,m,g),f.splice(l,0,m)}};\nfunction md(a,b,c){b.__shady=b.__shady||{};var d=b.__shady.ga;b.__shady.ga=null;c||(c=(a=a.l[b.slot||\"__catchall\"])&&a[0]);c?(c.__shady.assignedNodes.push(b),b.__shady.assignedSlot=c):b.__shady.assignedSlot=void 0;d!==b.__shady.assignedSlot&&b.__shady.assignedSlot&&(b.__shady.assignedSlot.__shady.ia=!0)}function nd(a,b,c){for(var d=0,e;d<c.length&&(e=c[d]);d++)if(\"slot\"==e.localName){var f=e.__shady.assignedNodes;f&&f.length&&nd(a,b,f)}else b.push(c[d])}\nfunction od(a,b){A.dispatchEvent.call(b,new Event(\"slotchange\"));b.__shady.assignedSlot&&od(a,b.__shady.assignedSlot)}function Fc(a){if(a.H.length){for(var b=a.H,c,d=0;d<b.length;d++){var e=b[d];e.__shady=e.__shady||{};xc(e);xc(e.parentNode);var f=Ic(e);a.l[f]?(c=c||{},c[f]=!0,a.l[f].push(e)):a.l[f]=[e];a.o.push(e)}if(c)for(var h in c)a.l[h]=Jc(a.l[h]);a.H=[]}}function Ic(a){var b=a.name||a.getAttribute(\"name\")||\"__catchall\";return a.Aa=b}\nfunction Jc(a){return a.sort(function(a,c){a=pd(a);for(var b=pd(c),e=0;e<a.length;e++){c=a[e];var f=b[e];if(c!==f)return a=Array.from(c.parentNode.childNodes),a.indexOf(c)-a.indexOf(f)}})}function pd(a){var b=[];do b.unshift(a);while(a=a.parentNode);return b}function Gc(a){Fc(a);return!!a.o.length}H.prototype.addEventListener=function(a,b,c){\"object\"!==typeof c&&(c={capture:!!c});c.aa=this;this.host.addEventListener(a,b,c)};\nH.prototype.removeEventListener=function(a,b,c){\"object\"!==typeof c&&(c={capture:!!c});c.aa=this;this.host.removeEventListener(a,b,c)};H.prototype.getElementById=function(a){return Lc(this,function(b){return b.id==a},function(a){return!!a})[0]||null};var qd=H.prototype;F(qd,rc,!0);F(qd,tc,!0);function rd(a){var b=a.getRootNode();z(b)&&ld(b);return a.__shady&&a.__shady.assignedSlot||null}\nvar sd={addEventListener:ad.bind(window),removeEventListener:cd.bind(window)},td={addEventListener:ad,removeEventListener:cd,appendChild:function(a){return yc(this,a)},insertBefore:function(a,b){return yc(this,a,b)},removeChild:function(a){return zc(this,a)},replaceChild:function(a,b){yc(this,a,b);zc(this,b);return a},cloneNode:function(a){if(\"template\"==this.localName)var b=A.cloneNode.call(this,a);else if(b=A.cloneNode.call(this,!1),a){a=this.childNodes;for(var c=0,d;c<a.length;c++)d=a[c].cloneNode(!0),\nb.appendChild(d)}return b},getRootNode:function(){return Kc(this)},contains:function(a){return Hb(this,a)},get isConnected(){var a=this.ownerDocument;if(Gb&&A.contains.call(a,this)||a.documentElement&&A.contains.call(a.documentElement,this))return!0;for(a=this;a&&!(a instanceof Document);)a=a.parentNode||(a instanceof H?a.host:void 0);return!!(a&&a instanceof Document)},dispatchEvent:function(a){Lb();return A.dispatchEvent.call(this,a)}},ud={get assignedSlot(){return rd(this)}},vd={querySelector:function(a){return Lc(this,\nfunction(b){return yb.call(b,a)},function(a){return!!a})[0]||null},querySelectorAll:function(a){return Lc(this,function(b){return yb.call(b,a)})}},wd={assignedNodes:function(a){if(\"slot\"===this.localName){var b=this.getRootNode();z(b)&&ld(b);return this.__shady?(a&&a.flatten?this.__shady.K:this.__shady.assignedNodes)||[]:[]}}},xd=Ab({setAttribute:function(a,b){Oc(this,a,b)},removeAttribute:function(a){A.removeAttribute.call(this,a);Hc(this,a)},attachShadow:function(a){if(!this)throw\"Must provide a host.\";\nif(!a)throw\"Not enough arguments.\";return new H(kd,this,a)},get slot(){return this.getAttribute(\"slot\")},set slot(a){Oc(this,\"slot\",a)},get assignedSlot(){return rd(this)}},vd,wd);Object.defineProperties(xd,sc);var yd=Ab({importNode:function(a,b){return Pc(a,b)},getElementById:function(a){return Lc(this,function(b){return b.id==a},function(a){return!!a})[0]||null}},vd);Object.defineProperties(yd,{_activeElement:tc.activeElement});\nvar zd=HTMLElement.prototype.blur,Ad=Ab({blur:function(){var a=this.__shady&&this.__shady.root;(a=a&&a.activeElement)?a.blur():zd.call(this)}});function I(a,b){for(var c=Object.getOwnPropertyNames(b),d=0;d<c.length;d++){var e=c[d],f=Object.getOwnPropertyDescriptor(b,e);f.value?a[e]=f.value:Object.defineProperty(a,e,f)}};if(y.sa){var ShadyDOM={inUse:y.sa,patch:function(a){return a},isShadyRoot:z,enqueue:Kb,flush:Lb,settings:y,filterMutations:Rb,observeChildren:Pb,unobserveChildren:Qb,nativeMethods:A,nativeTree:B};window.ShadyDOM=ShadyDOM;window.Event=ed;window.CustomEvent=fd;window.MouseEvent=gd;dd();var Bd=window.customElements&&window.customElements.nativeHTMLElement||HTMLElement;I(window.Node.prototype,td);I(window.Window.prototype,sd);I(window.Text.prototype,ud);I(window.DocumentFragment.prototype,vd);I(window.Element.prototype,\nxd);I(window.Document.prototype,yd);window.HTMLSlotElement&&I(window.HTMLSlotElement.prototype,wd);I(Bd.prototype,Ad);y.M&&(G(window.Node.prototype),G(window.Text.prototype),G(window.DocumentFragment.prototype),G(window.Element.prototype),G(Bd.prototype),G(window.Document.prototype),window.HTMLSlotElement&&G(window.HTMLSlotElement.prototype));window.ShadowRoot=H};var Cd=new Set(\"annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph\".split(\" \"));function Dd(a){var b=Cd.has(a);a=/^[a-z][.0-9_a-z]*-[\\-.0-9_a-z]*$/.test(a);return!b&&a}function K(a){var b=a.isConnected;if(void 0!==b)return b;for(;a&&!(a.__CE_isImportDocument||a instanceof Document);)a=a.parentNode||(window.ShadowRoot&&a instanceof ShadowRoot?a.host:void 0);return!(!a||!(a.__CE_isImportDocument||a instanceof Document))}\nfunction Ed(a,b){for(;b&&b!==a&&!b.nextSibling;)b=b.parentNode;return b&&b!==a?b.nextSibling:null}\nfunction L(a,b,c){c=void 0===c?new Set:c;for(var d=a;d;){if(d.nodeType===Node.ELEMENT_NODE){var e=d;b(e);var f=e.localName;if(\"link\"===f&&\"import\"===e.getAttribute(\"rel\")){d=e.import;if(d instanceof Node&&!c.has(d))for(c.add(d),d=d.firstChild;d;d=d.nextSibling)L(d,b,c);d=Ed(a,e);continue}else if(\"template\"===f){d=Ed(a,e);continue}if(e=e.__CE_shadowRoot)for(e=e.firstChild;e;e=e.nextSibling)L(e,b,c)}d=d.firstChild?d.firstChild:Ed(a,d)}}function M(a,b,c){a[b]=c};function Fd(){this.a=new Map;this.s=new Map;this.h=[];this.c=!1}function Gd(a,b,c){a.a.set(b,c);a.s.set(c.constructor,c)}function Hd(a,b){a.c=!0;a.h.push(b)}function Id(a,b){a.c&&L(b,function(b){return a.b(b)})}Fd.prototype.b=function(a){if(this.c&&!a.__CE_patched){a.__CE_patched=!0;for(var b=0;b<this.h.length;b++)this.h[b](a)}};function N(a,b){var c=[];L(b,function(a){return c.push(a)});for(b=0;b<c.length;b++){var d=c[b];1===d.__CE_state?a.connectedCallback(d):Jd(a,d)}}\nfunction O(a,b){var c=[];L(b,function(a){return c.push(a)});for(b=0;b<c.length;b++){var d=c[b];1===d.__CE_state&&a.disconnectedCallback(d)}}\nfunction P(a,b,c){c=void 0===c?{}:c;var d=c.$a||new Set,e=c.va||function(b){return Jd(a,b)},f=[];L(b,function(b){if(\"link\"===b.localName&&\"import\"===b.getAttribute(\"rel\")){var c=b.import;c instanceof Node&&(c.__CE_isImportDocument=!0,c.__CE_hasRegistry=!0);c&&\"complete\"===c.readyState?c.__CE_documentLoadHandled=!0:b.addEventListener(\"load\",function(){var c=b.import;if(!c.__CE_documentLoadHandled){c.__CE_documentLoadHandled=!0;var f=new Set(d);f.delete(c);P(a,c,{$a:f,va:e})}})}else f.push(b)},d);if(a.c)for(b=\n0;b<f.length;b++)a.b(f[b]);for(b=0;b<f.length;b++)e(f[b])}\nfunction Jd(a,b){if(void 0===b.__CE_state){var c=b.ownerDocument;if(c.defaultView||c.__CE_isImportDocument&&c.__CE_hasRegistry)if(c=a.a.get(b.localName)){c.constructionStack.push(b);var d=c.constructor;try{try{if(new d!==b)throw Error(\"The custom element constructor did not produce the element being upgraded.\");}finally{c.constructionStack.pop()}}catch(h){throw b.__CE_state=2,h;}b.__CE_state=1;b.__CE_definition=c;if(c.attributeChangedCallback)for(c=c.observedAttributes,d=0;d<c.length;d++){var e=c[d],\nf=b.getAttribute(e);null!==f&&a.attributeChangedCallback(b,e,null,f,null)}K(b)&&a.connectedCallback(b)}}}Fd.prototype.connectedCallback=function(a){var b=a.__CE_definition;b.connectedCallback&&b.connectedCallback.call(a)};Fd.prototype.disconnectedCallback=function(a){var b=a.__CE_definition;b.disconnectedCallback&&b.disconnectedCallback.call(a)};\nFd.prototype.attributeChangedCallback=function(a,b,c,d,e){var f=a.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(b)&&f.attributeChangedCallback.call(a,b,c,d,e)};function Kd(a){var b=document;this.j=a;this.a=b;this.C=void 0;P(this.j,this.a);\"loading\"===this.a.readyState&&(this.C=new MutationObserver(this.b.bind(this)),this.C.observe(this.a,{childList:!0,subtree:!0}))}Kd.prototype.disconnect=function(){this.C&&this.C.disconnect()};Kd.prototype.b=function(a){var b=this.a.readyState;\"interactive\"!==b&&\"complete\"!==b||this.disconnect();for(b=0;b<a.length;b++)for(var c=a[b].addedNodes,d=0;d<c.length;d++)P(this.j,c[d])};function Ld(){var a=this;this.b=this.a=void 0;this.c=new Promise(function(b){a.b=b;a.a&&b(a.a)})}Ld.prototype.resolve=function(a){if(this.a)throw Error(\"Already resolved.\");this.a=a;this.b&&this.b(a)};function Q(a){this.da=!1;this.j=a;this.ha=new Map;this.ea=function(a){return a()};this.R=!1;this.fa=[];this.Da=new Kd(a)}\nQ.prototype.define=function(a,b){var c=this;if(!(b instanceof Function))throw new TypeError(\"Custom element constructors must be functions.\");if(!Dd(a))throw new SyntaxError(\"The element name '\"+a+\"' is not valid.\");if(this.j.a.get(a))throw Error(\"A custom element with name '\"+a+\"' has already been defined.\");if(this.da)throw Error(\"A custom element is already being defined.\");this.da=!0;try{var d=function(a){var b=e[a];if(void 0!==b&&!(b instanceof Function))throw Error(\"The '\"+a+\"' callback must be a function.\");\nreturn b},e=b.prototype;if(!(e instanceof Object))throw new TypeError(\"The custom element constructor's prototype is not an object.\");var f=d(\"connectedCallback\");var h=d(\"disconnectedCallback\");var g=d(\"adoptedCallback\");var k=d(\"attributeChangedCallback\");var l=b.observedAttributes||[]}catch(m){return}finally{this.da=!1}b={localName:a,constructor:b,connectedCallback:f,disconnectedCallback:h,adoptedCallback:g,attributeChangedCallback:k,observedAttributes:l,constructionStack:[]};Gd(this.j,a,b);this.fa.push(b);\nthis.R||(this.R=!0,this.ea(function(){return Md(c)}))};function Md(a){if(!1!==a.R){a.R=!1;for(var b=a.fa,c=[],d=new Map,e=0;e<b.length;e++)d.set(b[e].localName,[]);P(a.j,document,{va:function(b){if(void 0===b.__CE_state){var e=b.localName,f=d.get(e);f?f.push(b):a.j.a.get(e)&&c.push(b)}}});for(e=0;e<c.length;e++)Jd(a.j,c[e]);for(;0<b.length;){var f=b.shift();e=f.localName;f=d.get(f.localName);for(var h=0;h<f.length;h++)Jd(a.j,f[h]);(e=a.ha.get(e))&&e.resolve(void 0)}}}\nQ.prototype.get=function(a){if(a=this.j.a.get(a))return a.constructor};Q.prototype.a=function(a){if(!Dd(a))return Promise.reject(new SyntaxError(\"'\"+a+\"' is not a valid custom element name.\"));var b=this.ha.get(a);if(b)return b.c;b=new Ld;this.ha.set(a,b);this.j.a.get(a)&&!this.fa.some(function(b){return b.localName===a})&&b.resolve(void 0);return b.c};Q.prototype.b=function(a){this.Da.disconnect();var b=this.ea;this.ea=function(c){return a(function(){return b(c)})}};\nwindow.CustomElementRegistry=Q;Q.prototype.define=Q.prototype.define;Q.prototype.get=Q.prototype.get;Q.prototype.whenDefined=Q.prototype.a;Q.prototype.polyfillWrapFlushCallback=Q.prototype.b;var Nd=window.Document.prototype.createElement,Od=window.Document.prototype.createElementNS,Pd=window.Document.prototype.importNode,Qd=window.Document.prototype.prepend,Rd=window.Document.prototype.append,Sd=window.DocumentFragment.prototype.prepend,Td=window.DocumentFragment.prototype.append,Ud=window.Node.prototype.cloneNode,Vd=window.Node.prototype.appendChild,Wd=window.Node.prototype.insertBefore,Xd=window.Node.prototype.removeChild,Yd=window.Node.prototype.replaceChild,Zd=Object.getOwnPropertyDescriptor(window.Node.prototype,\n\"textContent\"),$d=window.Element.prototype.attachShadow,ae=Object.getOwnPropertyDescriptor(window.Element.prototype,\"innerHTML\"),be=window.Element.prototype.getAttribute,ce=window.Element.prototype.setAttribute,de=window.Element.prototype.removeAttribute,ee=window.Element.prototype.getAttributeNS,fe=window.Element.prototype.setAttributeNS,ge=window.Element.prototype.removeAttributeNS,he=window.Element.prototype.insertAdjacentElement,ie=window.Element.prototype.prepend,je=window.Element.prototype.append,\nke=window.Element.prototype.before,le=window.Element.prototype.after,me=window.Element.prototype.replaceWith,ne=window.Element.prototype.remove,oe=window.HTMLElement,pe=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,\"innerHTML\"),qe=window.HTMLElement.prototype.insertAdjacentElement;var re=new function(){};function se(){var a=te;window.HTMLElement=function(){function b(){var b=this.constructor,d=a.s.get(b);if(!d)throw Error(\"The custom element being constructed was not registered with `customElements`.\");var e=d.constructionStack;if(0===e.length)return e=Nd.call(document,d.localName),Object.setPrototypeOf(e,b.prototype),e.__CE_state=1,e.__CE_definition=d,a.b(e),e;d=e.length-1;var f=e[d];if(f===re)throw Error(\"The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.\");\ne[d]=re;Object.setPrototypeOf(f,b.prototype);a.b(f);return f}b.prototype=oe.prototype;return b}()};function ue(a,b,c){function d(b){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var f=[],l=0;l<d.length;l++){var m=d[l];m instanceof Element&&K(m)&&f.push(m);if(m instanceof DocumentFragment)for(m=m.firstChild;m;m=m.nextSibling)e.push(m);else e.push(m)}b.apply(this,d);for(d=0;d<f.length;d++)O(a,f[d]);if(K(this))for(d=0;d<e.length;d++)f=e[d],f instanceof Element&&N(a,f)}}void 0!==c.X&&(b.prepend=d(c.X));void 0!==c.append&&(b.append=d(c.append))};function ve(){var a=te;M(Document.prototype,\"createElement\",function(b){if(this.__CE_hasRegistry){var c=a.a.get(b);if(c)return new c.constructor}b=Nd.call(this,b);a.b(b);return b});M(Document.prototype,\"importNode\",function(b,c){b=Pd.call(this,b,c);this.__CE_hasRegistry?P(a,b):Id(a,b);return b});M(Document.prototype,\"createElementNS\",function(b,c){if(this.__CE_hasRegistry&&(null===b||\"http://www.w3.org/1999/xhtml\"===b)){var d=a.a.get(c);if(d)return new d.constructor}b=Od.call(this,b,c);a.b(b);return b});\nue(a,Document.prototype,{X:Qd,append:Rd})};function we(){var a=te;function b(b,d){Object.defineProperty(b,\"textContent\",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(b){if(this.nodeType===Node.TEXT_NODE)d.set.call(this,b);else{var c=void 0;if(this.firstChild){var e=this.childNodes,g=e.length;if(0<g&&K(this)){c=Array(g);for(var k=0;k<g;k++)c[k]=e[k]}}d.set.call(this,b);if(c)for(b=0;b<c.length;b++)O(a,c[b])}}})}M(Node.prototype,\"insertBefore\",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);\nb=Wd.call(this,b,d);if(K(this))for(d=0;d<c.length;d++)N(a,c[d]);return b}c=K(b);d=Wd.call(this,b,d);c&&O(a,b);K(this)&&N(a,b);return d});M(Node.prototype,\"appendChild\",function(b){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Vd.call(this,b);if(K(this))for(var e=0;e<c.length;e++)N(a,c[e]);return b}c=K(b);e=Vd.call(this,b);c&&O(a,b);K(this)&&N(a,b);return e});M(Node.prototype,\"cloneNode\",function(b){b=Ud.call(this,b);this.ownerDocument.__CE_hasRegistry?P(a,b):\nId(a,b);return b});M(Node.prototype,\"removeChild\",function(b){var c=K(b),e=Xd.call(this,b);c&&O(a,b);return e});M(Node.prototype,\"replaceChild\",function(b,d){if(b instanceof DocumentFragment){var c=Array.prototype.slice.apply(b.childNodes);b=Yd.call(this,b,d);if(K(this))for(O(a,d),d=0;d<c.length;d++)N(a,c[d]);return b}c=K(b);var f=Yd.call(this,b,d),h=K(this);h&&O(a,d);c&&O(a,b);h&&N(a,b);return f});Zd&&Zd.get?b(Node.prototype,Zd):Hd(a,function(a){b(a,{enumerable:!0,configurable:!0,get:function(){for(var a=\n[],b=0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join(\"\")},set:function(a){for(;this.firstChild;)Xd.call(this,this.firstChild);Vd.call(this,document.createTextNode(a))}})})};function xe(a){var b=Element.prototype;function c(b){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];e=[];for(var g=[],k=0;k<d.length;k++){var l=d[k];l instanceof Element&&K(l)&&g.push(l);if(l instanceof DocumentFragment)for(l=l.firstChild;l;l=l.nextSibling)e.push(l);else e.push(l)}b.apply(this,d);for(d=0;d<g.length;d++)O(a,g[d]);if(K(this))for(d=0;d<e.length;d++)g=e[d],g instanceof Element&&N(a,g)}}void 0!==ke&&(b.before=c(ke));void 0!==ke&&(b.after=c(le));void 0!==\nme&&M(b,\"replaceWith\",function(b){for(var c=[],d=0;d<arguments.length;++d)c[d-0]=arguments[d];d=[];for(var h=[],g=0;g<c.length;g++){var k=c[g];k instanceof Element&&K(k)&&h.push(k);if(k instanceof DocumentFragment)for(k=k.firstChild;k;k=k.nextSibling)d.push(k);else d.push(k)}g=K(this);me.apply(this,c);for(c=0;c<h.length;c++)O(a,h[c]);if(g)for(O(a,this),c=0;c<d.length;c++)h=d[c],h instanceof Element&&N(a,h)});void 0!==ne&&M(b,\"remove\",function(){var b=K(this);ne.call(this);b&&O(a,this)})};function ye(){var a=te;function b(b,c){Object.defineProperty(b,\"innerHTML\",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(b){var d=this,e=void 0;K(this)&&(e=[],L(this,function(a){a!==d&&e.push(a)}));c.set.call(this,b);if(e)for(var f=0;f<e.length;f++){var l=e[f];1===l.__CE_state&&a.disconnectedCallback(l)}this.ownerDocument.__CE_hasRegistry?P(a,this):Id(a,this);return b}})}function c(b,c){M(b,\"insertAdjacentElement\",function(b,d){var e=K(d);b=c.call(this,b,d);e&&O(a,d);K(b)&&N(a,d);\nreturn b})}$d&&M(Element.prototype,\"attachShadow\",function(a){return this.__CE_shadowRoot=a=$d.call(this,a)});ae&&ae.get?b(Element.prototype,ae):pe&&pe.get?b(HTMLElement.prototype,pe):Hd(a,function(a){b(a,{enumerable:!0,configurable:!0,get:function(){return Ud.call(this,!0).innerHTML},set:function(a){var b=\"template\"===this.localName,c=b?this.content:this,d=Nd.call(document,this.localName);for(d.innerHTML=a;0<c.childNodes.length;)Xd.call(c,c.childNodes[0]);for(a=b?d.content:d;0<a.childNodes.length;)Vd.call(c,\na.childNodes[0])}})});M(Element.prototype,\"setAttribute\",function(b,c){if(1!==this.__CE_state)return ce.call(this,b,c);var d=be.call(this,b);ce.call(this,b,c);c=be.call(this,b);a.attributeChangedCallback(this,b,d,c,null)});M(Element.prototype,\"setAttributeNS\",function(b,c,f){if(1!==this.__CE_state)return fe.call(this,b,c,f);var d=ee.call(this,b,c);fe.call(this,b,c,f);f=ee.call(this,b,c);a.attributeChangedCallback(this,c,d,f,b)});M(Element.prototype,\"removeAttribute\",function(b){if(1!==this.__CE_state)return de.call(this,\nb);var c=be.call(this,b);de.call(this,b);null!==c&&a.attributeChangedCallback(this,b,c,null,null)});M(Element.prototype,\"removeAttributeNS\",function(b,c){if(1!==this.__CE_state)return ge.call(this,b,c);var d=ee.call(this,b,c);ge.call(this,b,c);var e=ee.call(this,b,c);d!==e&&a.attributeChangedCallback(this,c,d,e,b)});qe?c(HTMLElement.prototype,qe):he?c(Element.prototype,he):console.warn(\"Custom Elements: `Element#insertAdjacentElement` was not patched.\");ue(a,Element.prototype,{X:ie,append:je});xe(a)}\n;var ze=window.customElements;if(!ze||ze.forcePolyfill||\"function\"!=typeof ze.define||\"function\"!=typeof ze.get){var te=new Fd;se();ve();ue(te,DocumentFragment.prototype,{X:Sd,append:Td});we();ye();document.__CE_hasRegistry=!0;var customElements=new Q(te);Object.defineProperty(window,\"customElements\",{configurable:!0,enumerable:!0,value:customElements})};function Ae(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText=\"\";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=\"\"}\nfunction Be(a){a=a.replace(Ce,\"\").replace(De,\"\");var b=Ee,c=a,d=new Ae;d.start=0;d.end=c.length;for(var e=d,f=0,h=c.length;f<h;f++)if(\"{\"===c[f]){e.rules||(e.rules=[]);var g=e,k=g.rules[g.rules.length-1]||null;e=new Ae;e.start=f+1;e.parent=g;e.previous=k;g.rules.push(e)}else\"}\"===c[f]&&(e.end=f+1,e=e.parent||d);return b(d,a)}\nfunction Ee(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=Fe(c),c=c.replace(Ge,\" \"),c=c.substring(c.lastIndexOf(\";\")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf(\"@\"),a.atRule?0===c.indexOf(\"@media\")?a.type=He:c.match(Ie)&&(a.type=Je,a.keyframesName=a.selector.split(Ge).pop()):a.type=0===c.indexOf(\"--\")?Ke:Le);if(c=a.rules)for(var d=0,e=c.length,f;d<e&&(f=c[d]);d++)Ee(f,\nb);return a}function Fe(a){return a.replace(/\\\\([0-9a-f]{1,6})\\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a=\"0\"+a;return\"\\\\\"+a})}\nfunction Me(a,b,c){c=void 0===c?\"\":c;var d=\"\";if(a.cssText||a.rules){var e=a.rules,f;if(f=e)f=e[0],f=!(f&&f.selector&&0===f.selector.indexOf(\"--\"));if(f){f=0;for(var h=e.length,g;f<h&&(g=e[f]);f++)d=Me(g,b,d)}else b?b=a.cssText:(b=a.cssText,b=b.replace(Ne,\"\").replace(Oe,\"\"),b=b.replace(Pe,\"\").replace(Qe,\"\")),(d=b.trim())&&(d=\"  \"+d+\"\\n\")}d&&(a.selector&&(c+=a.selector+\" {\\n\"),c+=d,a.selector&&(c+=\"}\\n\\n\"));return c}\nvar Le=1,Je=7,He=4,Ke=1E3,Ce=/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//gim,De=/@import[^;]*;/gim,Ne=/(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\\n]|$)/gim,Oe=/(?:^[^;\\-\\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\\n]|$)?/gim,Pe=/@apply\\s*\\(?[^);]*\\)?\\s*(?:[;\\n]|$)?/gim,Qe=/[^;:]*?:[^;]*?var\\([^;]*\\)(?:[;\\n]|$)?/gim,Ie=/^@[^\\s]*keyframes/,Ge=/\\s+/g;var R=!(window.ShadyDOM&&window.ShadyDOM.inUse),Re;function Se(a){Re=a&&a.shimcssproperties?!1:R||!(navigator.userAgent.match(/AppleWebKit\\/601|Edge\\/15/)||!window.CSS||!CSS.supports||!CSS.supports(\"box-shadow\",\"0 0 0 var(--foo)\"))}window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?Re=window.ShadyCSS.nativeCss:window.ShadyCSS?(Se(window.ShadyCSS),window.ShadyCSS=void 0):Se(window.WebComponents&&window.WebComponents.flags);var S=Re;var Te=/(?:^|[;\\s{]\\s*)(--[\\w-]*?)\\s*:\\s*(?:((?:'(?:\\\\'|.)*?'|\"(?:\\\\\"|.)*?\"|\\([^)]*?\\)|[^};{])+)|\\{([^}]*)\\}(?:(?=[;\\s}])|$))/gi,Ue=/(?:^|\\W+)@apply\\s*\\(?([^);\\n]*)\\)?/gi,Ve=/(--[\\w-]+)\\s*([:,;)]|$)/gi,We=/(animation\\s*:)|(animation-name\\s*:)/,Xe=/@media\\s(.*)/,Ye=/\\{[^}]*\\}/g;var Ze=new Set;function $e(a,b){if(!a)return\"\";\"string\"===typeof a&&(a=Be(a));b&&af(a,b);return Me(a,S)}function bf(a){!a.__cssRules&&a.textContent&&(a.__cssRules=Be(a.textContent));return a.__cssRules||null}function cf(a){return!!a.parent&&a.parent.type===Je}function af(a,b,c,d){if(a){var e=!1,f=a.type;if(d&&f===He){var h=a.selector.match(Xe);h&&(window.matchMedia(h[1]).matches||(e=!0))}f===Le?b(a):c&&f===Je?c(a):f===Ke&&(e=!0);if((a=a.rules)&&!e){e=0;f=a.length;for(var g;e<f&&(g=a[e]);e++)af(g,b,c,d)}}}\nfunction df(a,b,c,d){var e=document.createElement(\"style\");b&&e.setAttribute(\"scope\",b);e.textContent=a;ef(e,c,d);return e}var T=null;function ef(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);T?a.compareDocumentPosition(T)===Node.DOCUMENT_POSITION_PRECEDING&&(T=a):T=a}\nfunction ff(a,b){var c=a.indexOf(\"var(\");if(-1===c)return b(a,\"\",\"\",\"\");a:{var d=0;var e=c+3;for(var f=a.length;e<f;e++)if(\"(\"===a[e])d++;else if(\")\"===a[e]&&0===--d)break a;e=-1}d=a.substring(c+4,e);c=a.substring(0,c);a=ff(a.substring(e+1),b);e=d.indexOf(\",\");return-1===e?b(c,d.trim(),\"\",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function gf(a,b){R?a.setAttribute(\"class\",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,\"class\",b)}\nfunction V(a){var b=a.localName,c=\"\";b?-1<b.indexOf(\"-\")||(c=b,b=a.getAttribute&&a.getAttribute(\"is\")||\"\"):(b=a.is,c=a.extends);return{is:b,P:c}};function hf(){}function jf(a,b,c){var d=W;a.__styleScoped?a.__styleScoped=null:kf(d,a,b||\"\",c)}function kf(a,b,c,d){b.nodeType===Node.ELEMENT_NODE&&lf(b,c,d);if(b=\"template\"===b.localName?(b.content||b.gb).childNodes:b.children||b.childNodes)for(var e=0;e<b.length;e++)kf(a,b[e],c,d)}\nfunction lf(a,b,c){if(b)if(a.classList)c?(a.classList.remove(\"style-scope\"),a.classList.remove(b)):(a.classList.add(\"style-scope\"),a.classList.add(b));else if(a.getAttribute){var d=a.getAttribute(mf);c?d&&(b=d.replace(\"style-scope\",\"\").replace(b,\"\"),gf(a,b)):gf(a,(d?d+\" \":\"\")+\"style-scope \"+b)}}function nf(a,b,c){var d=W,e=a.__cssBuild;R||\"shady\"===e?b=$e(b,c):(a=V(a),b=of(d,b,a.is,a.P,c)+\"\\n\\n\");return b.trim()}\nfunction of(a,b,c,d,e){var f=pf(c,d);c=c?qf+c:\"\";return $e(b,function(b){b.c||(b.selector=b.m=rf(a,b,a.b,c,f),b.c=!0);e&&e(b,c,f)})}function pf(a,b){return b?\"[is=\"+a+\"]\":a}function rf(a,b,c,d,e){var f=b.selector.split(sf);if(!cf(b)){b=0;for(var h=f.length,g;b<h&&(g=f[b]);b++)f[b]=c.call(a,g,d,e)}return f.join(sf)}function tf(a){return a.replace(uf,function(a,c,d){-1<d.indexOf(\"+\")?d=d.replace(/\\+/g,\"___\"):-1<d.indexOf(\"___\")&&(d=d.replace(/___/g,\"+\"));return\":\"+c+\"(\"+d+\")\"})}\nhf.prototype.b=function(a,b,c){var d=!1;a=a.trim();var e=uf.test(a);e&&(a=a.replace(uf,function(a,b,c){return\":\"+b+\"(\"+c.replace(/\\s/g,\"\")+\")\"}),a=tf(a));a=a.replace(vf,wf+\" $1\");a=a.replace(xf,function(a,e,g){d||(a=yf(g,e,b,c),d=d||a.stop,e=a.Ka,g=a.value);return e+g});e&&(a=tf(a));return a};\nfunction yf(a,b,c,d){var e=a.indexOf(zf);0<=a.indexOf(wf)?a=Af(a,d):0!==e&&(a=c?Bf(a,c):a);c=!1;0<=e&&(b=\"\",c=!0);if(c){var f=!0;c&&(a=a.replace(Cf,function(a,b){return\" > \"+b}))}a=a.replace(Df,function(a,b,c){return'[dir=\"'+c+'\"] '+b+\", \"+b+'[dir=\"'+c+'\"]'});return{value:a,Ka:b,stop:f}}function Bf(a,b){a=a.split(Ef);a[0]+=b;return a.join(Ef)}\nfunction Af(a,b){var c=a.match(Ff);return(c=c&&c[2].trim()||\"\")?c[0].match(Gf)?a.replace(Ff,function(a,c,f){return b+f}):c.split(Gf)[0]===b?c:Hf:a.replace(wf,b)}function If(a){a.selector===Jf&&(a.selector=\"html\")}hf.prototype.c=function(a){return a.match(zf)?this.b(a,Kf):Bf(a.trim(),Kf)};q.Object.defineProperties(hf.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return\"style-scope\"}}});\nvar uf=/:(nth[-\\w]+)\\(([^)]+)\\)/,Kf=\":not(.style-scope)\",sf=\",\",xf=/(^|[\\s>+~]+)((?:\\[.+?\\]|[^\\s>+~=[])+)/g,Gf=/[[.:#*]/,wf=\":host\",Jf=\":root\",zf=\"::slotted\",vf=new RegExp(\"^(\"+zf+\")\"),Ff=/(:host)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))/,Cf=/(?:::slotted)(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))/,Df=/(.*):dir\\((?:(ltr|rtl))\\)/,qf=\".\",Ef=\":\",mf=\"class\",Hf=\"should_not_match\",W=new hf;function Lf(a,b,c,d){this.w=a||null;this.b=b||null;this.ja=c||[];this.G=null;this.P=d||\"\";this.a=this.u=this.B=null}function X(a){return a?a.__styleInfo:null}function Mf(a,b){return a.__styleInfo=b}Lf.prototype.c=function(){return this.w};Lf.prototype._getStyleRules=Lf.prototype.c;var Nf,Of=window.Element.prototype;Nf=Of.matches||Of.matchesSelector||Of.mozMatchesSelector||Of.msMatchesSelector||Of.oMatchesSelector||Of.webkitMatchesSelector;var Pf=navigator.userAgent.match(\"Trident\");function Qf(){}function Rf(a){var b={},c=[],d=0;af(a,function(a){Sf(a);a.index=d++;a=a.i.cssText;for(var c;c=Ve.exec(a);){var e=c[1];\":\"!==c[2]&&(b[e]=!0)}},function(a){c.push(a)});a.b=c;a=[];for(var e in b)a.push(e);return a}\nfunction Sf(a){if(!a.i){var b={},c={};Tf(a,c)&&(b.v=c,a.rules=null);b.cssText=a.parsedCssText.replace(Ye,\"\").replace(Te,\"\");a.i=b}}function Tf(a,b){var c=a.i;if(c){if(c.v)return Object.assign(b,c.v),!0}else{c=a.parsedCssText;for(var d;a=Te.exec(c);){d=(a[2]||a[3]).trim();if(\"inherit\"!==d||\"unset\"!==d)b[a[1].trim()]=d;d=!0}return d}}\nfunction Uf(a,b,c){b&&(b=0<=b.indexOf(\";\")?Vf(a,b,c):ff(b,function(b,e,f,h){if(!e)return b+h;(e=Uf(a,c[e],c))&&\"initial\"!==e?\"apply-shim-inherit\"===e&&(e=\"inherit\"):e=Uf(a,c[f]||f,c)||f;return b+(e||\"\")+h}));return b&&b.trim()||\"\"}\nfunction Vf(a,b,c){b=b.split(\";\");for(var d=0,e,f;d<b.length;d++)if(e=b[d]){Ue.lastIndex=0;if(f=Ue.exec(e))e=Uf(a,c[f[1]],c);else if(f=e.indexOf(\":\"),-1!==f){var h=e.substring(f);h=h.trim();h=Uf(a,h,c)||h;e=e.substring(0,f)+h}b[d]=e&&e.lastIndexOf(\";\")===e.length-1?e.slice(0,-1):e||\"\"}return b.join(\";\")}\nfunction Wf(a,b){var c={},d=[];af(a,function(a){a.i||Sf(a);var e=a.m||a.parsedSelector;b&&a.i.v&&e&&Nf.call(b,e)&&(Tf(a,c),a=a.index,e=parseInt(a/32,10),d[e]=(d[e]||0)|1<<a%32)},null,!0);return{v:c,key:d}}\nfunction Xf(a,b,c,d){b.i||Sf(b);if(b.i.v){var e=V(a);a=e.is;e=e.P;e=a?pf(a,e):\"html\";var f=b.parsedSelector,h=\":host > *\"===f||\"html\"===f,g=0===f.indexOf(\":host\")&&!h;\"shady\"===c&&(h=f===e+\" > *.\"+e||-1!==f.indexOf(\"html\"),g=!h&&0===f.indexOf(e));\"shadow\"===c&&(h=\":host > *\"===f||\"html\"===f,g=g&&!h);if(h||g)c=e,g&&(R&&!b.m&&(b.m=rf(W,b,W.b,a?qf+a:\"\",e)),c=b.m||e),d({Xa:c,Qa:g,hb:h})}}\nfunction Yf(a,b){var c={},d={},e=b&&b.__cssBuild;af(b,function(b){Xf(a,b,e,function(e){Nf.call(a.b||a,e.Xa)&&(e.Qa?Tf(b,c):Tf(b,d))})},null,!0);return{Wa:d,Oa:c}}\nfunction Zf(a,b,c,d){var e=V(b),f=pf(e.is,e.P),h=new RegExp(\"(?:^|[^.#[:])\"+(b.extends?\"\\\\\"+f.slice(0,-1)+\"\\\\]\":f)+\"($|[.:[\\\\s>+~])\");e=X(b).w;var g=$f(e,d);return nf(b,e,function(b){var e=\"\";b.i||Sf(b);b.i.cssText&&(e=Vf(a,b.i.cssText,c));b.cssText=e;if(!R&&!cf(b)&&b.cssText){var k=e=b.cssText;null==b.ra&&(b.ra=We.test(e));if(b.ra)if(null==b.W){b.W=[];for(var n in g)k=g[n],k=k(e),e!==k&&(e=k,b.W.push(n))}else{for(n=0;n<b.W.length;++n)k=g[b.W[n]],e=k(e);k=e}b.cssText=k;b.m=b.m||b.selector;e=\".\"+d;\nn=b.m.split(\",\");k=0;for(var t=n.length,C;k<t&&(C=n[k]);k++)n[k]=C.match(h)?C.replace(f,e):e+\" \"+C;b.selector=n.join(\",\")}})}function $f(a,b){a=a.b;var c={};if(!R&&a)for(var d=0,e=a[d];d<a.length;e=a[++d]){var f=e,h=b;f.h=new RegExp(\"\\\\b\"+f.keyframesName+\"(?!\\\\B|-)\",\"g\");f.a=f.keyframesName+\"-\"+h;f.m=f.m||f.selector;f.selector=f.m.replace(f.keyframesName,f.a);c[e.keyframesName]=ag(e)}return c}function ag(a){return function(b){return b.replace(a.h,a.a)}}\nfunction bg(a,b){var c=cg,d=bf(a);a.textContent=$e(d,function(a){var d=a.cssText=a.parsedCssText;a.i&&a.i.cssText&&(d=d.replace(Ne,\"\").replace(Oe,\"\"),a.cssText=Vf(c,d,b))})}q.Object.defineProperties(Qf.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return\"x-scope\"}}});var cg=new Qf;var dg={},eg=window.customElements;if(eg&&!R){var fg=eg.define;eg.define=function(a,b,c){var d=document.createComment(\" Shady DOM styles for \"+a+\" \"),e=document.head;e.insertBefore(d,(T?T.nextSibling:null)||e.firstChild);T=d;dg[a]=d;return fg.call(eg,a,b,c)}};function gg(){this.cache={}}gg.prototype.store=function(a,b,c,d){var e=this.cache[a]||[];e.push({v:b,styleElement:c,u:d});100<e.length&&e.shift();this.cache[a]=e};gg.prototype.fetch=function(a,b,c){if(a=this.cache[a])for(var d=a.length-1;0<=d;d--){var e=a[d],f;a:{for(f=0;f<c.length;f++){var h=c[f];if(e.v[h]!==b[h]){f=!1;break a}}f=!0}if(f)return e}};function hg(){}\nfunction ig(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var d=0;d<c.addedNodes.length;d++){var e=c.addedNodes[d];if(e.nodeType===Node.ELEMENT_NODE){var f=e.getRootNode();var h=e;var g=[];h.classList?g=Array.from(h.classList):h instanceof window.SVGElement&&h.hasAttribute(\"class\")&&(g=h.getAttribute(\"class\").split(/\\s+/));h=g;g=h.indexOf(W.a);if((h=-1<g?h[g+1]:\"\")&&f===e.ownerDocument)jf(e,h,!0);else if(f.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&\n(f=f.host))if(f=V(f).is,h===f)for(e=window.ShadyDOM.nativeMethods.querySelectorAll.call(e,\":not(.\"+W.a+\")\"),f=0;f<e.length;f++)lf(e[f],h);else h&&jf(e,h,!0),jf(e,f)}}}}\nif(!R){var jg=new MutationObserver(ig),kg=function(a){jg.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)kg(document);else{var lg=function(){kg(document.body)};window.HTMLImports?window.HTMLImports.whenReady(lg):requestAnimationFrame(function(){if(\"loading\"===document.readyState){var a=function(){lg();document.removeEventListener(\"readystatechange\",a)};document.addEventListener(\"readystatechange\",a)}else lg()})}hg=function(){ig(jg.takeRecords())}}\nvar mg=hg;var ng={};var og=Promise.resolve();function pg(a){if(a=ng[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function qg(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function rg(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a.qa||(a.qa=!0,og.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a.qa=!1}))};var sg=null,tg=window.HTMLImports&&window.HTMLImports.whenReady||null,ug;function vg(a){requestAnimationFrame(function(){tg?tg(a):(sg||(sg=new Promise(function(a){ug=a}),\"complete\"===document.readyState?ug():document.addEventListener(\"readystatechange\",function(){\"complete\"===document.readyState&&ug()})),sg.then(function(){a&&a()}))})};var wg=new gg;function Y(){var a=this;this.L={};this.c=document.documentElement;var b=new Ae;b.rules=[];this.h=Mf(this.c,new Lf(b));this.s=!1;this.b=this.a=null;vg(function(){xg(a)})}p=Y.prototype;p.wa=function(){mg()};p.Ma=function(a){return bf(a)};p.Za=function(a){return $e(a)};\np.prepareTemplate=function(a,b,c){if(!a.Ia){a.Ia=!0;a.name=b;a.extends=c;ng[b]=a;var d=(d=a.content.querySelector(\"style\"))?d.getAttribute(\"css-build\")||\"\":\"\";var e=[];for(var f=a.content.querySelectorAll(\"style\"),h=0;h<f.length;h++){var g=f[h];if(g.hasAttribute(\"shady-unscoped\")){if(!R){var k=g.textContent;Ze.has(k)||(Ze.add(k),k=g.cloneNode(!0),document.head.appendChild(k));g.parentNode.removeChild(g)}}else e.push(g.textContent),g.parentNode.removeChild(g)}e=e.join(\"\").trim();c={is:b,extends:c,\neb:d};R||jf(a.content,b);xg(this);f=Ue.test(e)||Te.test(e);Ue.lastIndex=0;Te.lastIndex=0;e=Be(e);f&&S&&this.a&&this.a.transformRules(e,b);a._styleAst=e;a.a=d;d=[];S||(d=Rf(a._styleAst));if(!d.length||S)e=R?a.content:null,b=dg[b],f=nf(c,a._styleAst),b=f.length?df(f,c.is,e,b):void 0,a.pa=b;a.Ha=d}};\nfunction yg(a){!a.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(a.b=window.ShadyCSS.CustomStyleInterface,a.b.transformCallback=function(b){a.ua(b)},a.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.s)&&a.F()})})}function xg(a){!a.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(a.a=window.ShadyCSS.ApplyShim,a.a.invalidCallback=pg);yg(a)}\np.F=function(){xg(this);if(this.b){var a=this.b.processStyles();if(this.b.enqueued){if(S)for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);if(c&&S&&this.a){var d=bf(c);xg(this);this.a.transformRules(d);c.textContent=$e(d)}}else for(zg(this,this.c,this.h),b=0;b<a.length;b++)(c=this.b.getStyleForCustomStyle(a[b]))&&bg(c,this.h.B);this.b.enqueued=!1;this.s&&!S&&this.styleDocument()}}};\np.styleElement=function(a,b){var c=V(a).is,d=X(a);if(!d){var e=V(a);d=e.is;e=e.P;var f=dg[d];d=ng[d];if(d){var h=d._styleAst;var g=d.Ha}d=Mf(a,new Lf(h,f,g,e))}a!==this.c&&(this.s=!0);b&&(d.G=d.G||{},Object.assign(d.G,b));if(S){if(d.G){b=d.G;for(var k in b)null===k?a.style.removeProperty(k):a.style.setProperty(k,b[k])}if(((k=ng[c])||a===this.c)&&k&&k.pa&&!qg(k)){if(qg(k)||k._applyShimValidatingVersion!==k._applyShimNextVersion)xg(this),this.a&&this.a.transformRules(k._styleAst,c),k.pa.textContent=\nnf(a,d.w),rg(k);R&&(c=a.shadowRoot)&&(c.querySelector(\"style\").textContent=nf(a,d.w));d.w=k._styleAst}}else if(zg(this,a,d),d.ja&&d.ja.length){c=d;k=V(a).is;d=(b=wg.fetch(k,c.B,c.ja))?b.styleElement:null;h=c.u;(g=b&&b.u)||(g=this.L[k]=(this.L[k]||0)+1,g=k+\"-\"+g);c.u=g;g=c.u;e=cg;e=d?d.textContent||\"\":Zf(e,a,c.B,g);f=X(a);var l=f.a;l&&!R&&l!==d&&(l._useCount--,0>=l._useCount&&l.parentNode&&l.parentNode.removeChild(l));R?f.a?(f.a.textContent=e,d=f.a):e&&(d=df(e,g,a.shadowRoot,f.b)):d?d.parentNode||\n(Pf&&-1<e.indexOf(\"@media\")&&(d.textContent=e),ef(d,null,f.b)):e&&(d=df(e,g,null,f.b));d&&(d._useCount=d._useCount||0,f.a!=d&&d._useCount++,f.a=d);g=d;R||(d=c.u,f=e=a.getAttribute(\"class\")||\"\",h&&(f=e.replace(new RegExp(\"\\\\s*x-scope\\\\s*\"+h+\"\\\\s*\",\"g\"),\" \")),f+=(f?\" \":\"\")+\"x-scope \"+d,e!==f&&gf(a,f));b||wg.store(k,c.B,g,c.u)}};function Ag(a,b){return(b=b.getRootNode().host)?X(b)?b:Ag(a,b):a.c}\nfunction zg(a,b,c){a=Ag(a,b);var d=X(a);a=Object.create(d.B||null);var e=Yf(b,c.w);b=Wf(d.w,b).v;Object.assign(a,e.Oa,b,e.Wa);b=c.G;for(var f in b)if((e=b[f])||0===e)a[f]=e;f=cg;b=Object.getOwnPropertyNames(a);for(e=0;e<b.length;e++)d=b[e],a[d]=Uf(f,a[d],a);c.B=a}p.styleDocument=function(a){this.styleSubtree(this.c,a)};\np.styleSubtree=function(a,b){var c=a.shadowRoot;(c||a===this.c)&&this.styleElement(a,b);if(b=c&&(c.children||c.childNodes))for(a=0;a<b.length;a++)this.styleSubtree(b[a]);else if(a=a.children||a.childNodes)for(b=0;b<a.length;b++)this.styleSubtree(a[b])};p.ua=function(a){var b=this,c=bf(a);af(c,function(a){if(R)If(a);else{var c=W;a.selector=a.parsedSelector;If(a);a.selector=a.m=rf(c,a,c.c,void 0,void 0)}S&&(xg(b),b.a&&b.a.transformRule(a))});S?a.textContent=$e(c):this.h.w.rules.push(c)};\np.getComputedStyleValue=function(a,b){var c;S||(c=(X(a)||X(Ag(this,a))).B[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():\"\"};p.Ya=function(a,b){var c=a.getRootNode();b=b?b.split(/\\s/):[];c=c.host&&c.host.localName;if(!c){var d=a.getAttribute(\"class\");if(d){d=d.split(/\\s/);for(var e=0;e<d.length;e++)if(d[e]===W.a){c=d[e+1];break}}}c&&b.push(W.a,c);S||(c=X(a))&&c.u&&b.push(cg.a,c.u);gf(a,b.join(\" \"))};p.Ja=function(a){return X(a)};Y.prototype.flush=Y.prototype.wa;\nY.prototype.prepareTemplate=Y.prototype.prepareTemplate;Y.prototype.styleElement=Y.prototype.styleElement;Y.prototype.styleDocument=Y.prototype.styleDocument;Y.prototype.styleSubtree=Y.prototype.styleSubtree;Y.prototype.getComputedStyleValue=Y.prototype.getComputedStyleValue;Y.prototype.setElementClass=Y.prototype.Ya;Y.prototype._styleInfoForNode=Y.prototype.Ja;Y.prototype.transformCustomStyleForDocument=Y.prototype.ua;Y.prototype.getStyleAst=Y.prototype.Ma;Y.prototype.styleAstToString=Y.prototype.Za;\nY.prototype.flushCustomStyles=Y.prototype.F;Object.defineProperties(Y.prototype,{nativeShadow:{get:function(){return R}},nativeCss:{get:function(){return S}}});var Z=new Y,Bg,Cg;window.ShadyCSS&&(Bg=window.ShadyCSS.ApplyShim,Cg=window.ShadyCSS.CustomStyleInterface);window.ShadyCSS={ScopingShim:Z,prepareTemplate:function(a,b,c){Z.F();Z.prepareTemplate(a,b,c)},styleSubtree:function(a,b){Z.F();Z.styleSubtree(a,b)},styleElement:function(a){Z.F();Z.styleElement(a)},styleDocument:function(a){Z.F();Z.styleDocument(a)},getComputedStyleValue:function(a,b){return Z.getComputedStyleValue(a,b)},nativeCss:S,nativeShadow:R};Bg&&(window.ShadyCSS.ApplyShim=Bg);\nCg&&(window.ShadyCSS.CustomStyleInterface=Cg);var Dg=window.customElements,Eg=window.HTMLImports,Fg=window.HTMLTemplateElement;window.WebComponents=window.WebComponents||{};if(Dg&&Dg.polyfillWrapFlushCallback){var Gg,Hg=function(){if(Gg){Fg.J&&Fg.J(window.document);var a=Gg;Gg=null;a();return!0}},Ig=Eg.whenReady;Dg.polyfillWrapFlushCallback(function(a){Gg=a;Ig(Hg)});Eg.whenReady=function(a){Ig(function(){Hg()?Eg.whenReady(a):a()})}}\nEg.whenReady(function(){requestAnimationFrame(function(){window.WebComponents.ready=!0;document.dispatchEvent(new CustomEvent(\"WebComponentsReady\",{bubbles:!0}))})});var Jg=document.createElement(\"style\");Jg.textContent=\"body {transition: opacity ease-in 0.2s; } \\nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \\n\";var Kg=document.querySelector(\"head\");Kg.insertBefore(Jg,Kg.firstChild);}).call(this);\n\n//# sourceMappingURL=webcomponents-lite.js.map\n"
  },
  {
    "path": "src/common/channels/channels.actions.js",
    "content": "import { loadSample } from '../../services/sampleStore';\nimport { CHANNELS_CONSTANTS } from './channels.constants';\nimport { setNotes, initializeChannelNotes } from '../notes';\nimport { uuid } from '../../services/uuid';\nimport factorySamples from '../../samples.config';\nimport { setSelectedChannel } from '../master';\nimport { showFlashMessage, FLASH_MESSAGES } from '../window';\n\nexport const setChannelGain = (channel, gain) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_GAIN,\n  payload: {\n    channel,\n    gain,\n  },\n});\n\nexport const setChannelPan = (channel, pan) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_PAN,\n  payload: {\n    channel,\n    pan,\n  },\n});\n\nexport const setChannelPitchCoarse = (channel, pitchCoarse) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_PITCH_COARSE,\n  payload: {\n    channel,\n    pitchCoarse,\n  },\n});\n\nexport const setChannelPitchFine = (channel, pitchFine) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_PITCH_FINE,\n  payload: {\n    channel,\n    pitchFine,\n  },\n});\n\nexport const setChannelMuted = (channel, muted) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_MUTED,\n  payload: {\n    channel,\n    muted,\n  },\n});\n\nexport const setChannelSolo = (channel, solo) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_SOLO,\n  payload: {\n    channel,\n    solo,\n  },\n});\n\nexport const addChannel = (channel) => ({\n  type: CHANNELS_CONSTANTS.ADD_CHANNEL,\n  payload: channel,\n});\n\nexport const removeChannel = (id) => ({\n  type: CHANNELS_CONSTANTS.REMOVE_CHANNEL,\n  payload: id,\n});\n\nexport const updateChannelOrder = (oldIndex, newIndex) => ({\n  type: CHANNELS_CONSTANTS.UPDATE_CHANNEL_ORDER,\n  payload: {\n    oldIndex,\n    newIndex,\n  },\n});\n\nexport const replaceChannels = (channels) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNELS,\n  payload: channels,\n});\n\nexport const sampleLoaded = (channelID, isLoaded) => ({\n  type: CHANNELS_CONSTANTS.SAMPLE_LOADED,\n  payload: {\n    channelID,\n    isLoaded,\n  },\n});\n\nexport const setChannelSample = (channel, sampleURL) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_SAMPLE,\n  payload: {\n    channel,\n    sampleURL,\n  },\n});\n\nexport const setChannelReverb = (channel, reverb) => ({\n  type: CHANNELS_CONSTANTS.SET_CHANNEL_REVERB,\n  payload: {\n    channel,\n    reverb,\n  },\n});\n\nexport const loadSampleStatefully = (dispatch, channel) => {\n  dispatch(sampleLoaded(channel.id, false));\n  loadSample(channel.sample).then((success) => {\n    if (success) {\n      dispatch(sampleLoaded(channel.id, true));\n    }\n  });\n};\n\nexport const loadChannels = (channels, notes) => (dispatch) => {\n  channels.forEach((channel) => {\n    loadSampleStatefully(dispatch, channel);\n  });\n  dispatch(replaceChannels(channels));\n  dispatch(setNotes(notes));\n};\n\nexport const newChannel = () => (dispatch) => {\n  const channelToAdd = {\n    id: uuid(),\n    sample: factorySamples[0].url,\n    gain: 1,\n    pitchCoarse: 0,\n    pitchFine: 0,\n    pan: 0,\n  };\n  dispatch(addChannel(channelToAdd));\n  dispatch(initializeChannelNotes(channelToAdd.id));\n  dispatch(setSelectedChannel(channelToAdd.id));\n  loadSampleStatefully(dispatch, channelToAdd);\n};\n\nexport const loadAndSetChannelSample = (channelID, sampleURL) => (dispatch) => {\n  dispatch(sampleLoaded(channelID, false));\n  loadSample(sampleURL).then((success) => {\n    if (success) {\n      dispatch(sampleLoaded(channelID, true));\n    } else {\n      dispatch(showFlashMessage(FLASH_MESSAGES.SAMPLE_LOAD_ERROR));\n    }\n  });\n  dispatch(setChannelSample(channelID, sampleURL));\n};\n\nexport const deleteChannel = (channelID, channels, selectedChannelId) => (dispatch) => {\n  if (channels.length === 1) {\n    dispatch(newChannel());\n  }\n  if (selectedChannelId === channelID) {\n    dispatch(setSelectedChannel(channels[0].id));\n  }\n  dispatch(removeChannel(channelID));\n};\n"
  },
  {
    "path": "src/common/channels/channels.constants.js",
    "content": "export const CHANNELS_CONSTANTS = {\n  ADD_CHANNEL: 'ADD_CHANNEL',\n  REMOVE_CHANNEL: 'REMOVE_CHANNEL',\n  SET_CHANNEL_SAMPLE: 'SET_CHANNEL_SAMPLE',\n  SET_CHANNEL_PITCH_COARSE: 'SET_CHANNEL_PITCH_COARSE',\n  SET_CHANNEL_FINE: 'SET_CHANNEL_FINE',\n  SET_CHANNEL_GAIN: 'SET_CHANNEL_GAIN',\n  SET_CHANNEL_PAN: 'SET_CHANNEL_PAN',\n  SET_CHANNEL_REVERB: 'SET_CHANNEL_REVERB',\n  SET_CHANNEL_MUTED: 'SET_CHANNEL_MUTED',\n  SET_CHANNEL_SOLO: 'SET_CHANNEL_SOLO',\n  SET_CHANNELS: 'SET_CHANNELS',\n  SAMPLE_LOADED: 'SAMPLE_LOADED',\n  UPDATE_CHANNEL_ORDER: 'UPDATE_CHANNEL_ORDER',\n};\n"
  },
  {
    "path": "src/common/channels/channels.reducer.js",
    "content": "import * as R from 'ramda';\nimport { CHANNELS_CONSTANTS } from './channels.constants';\nimport presets from '../../presets';\n\nexport const channelsInitialState = R.clone(presets[1].channels);\n\nexport const channelsReducer = (state = channelsInitialState, action) => {\n  switch (action.type) {\n    case CHANNELS_CONSTANTS.SET_CHANNEL_SAMPLE:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, sample: action.payload.sampleURL };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_GAIN:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, gain: action.payload.gain };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_PAN:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, pan: action.payload.pan };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_PITCH_COARSE:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, pitchCoarse: action.payload.pitchCoarse };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_PITCH_FINE:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, pitchFine: action.payload.pitchFine };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_REVERB:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return { ...channel, reverb: action.payload.reverb };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.ADD_CHANNEL:\n      return [...state, action.payload];\n    case CHANNELS_CONSTANTS.REMOVE_CHANNEL:\n      return state.filter((channel) => channel.id !== action.payload);\n    case CHANNELS_CONSTANTS.SAMPLE_LOADED:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channelID) {\n          return { ...channel, sampleLoaded: action.payload.isLoaded };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_MUTED:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return {\n            ...channel,\n            muted: action.payload.muted,\n            solo: false,\n          };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.SET_CHANNEL_SOLO:\n      return state.map((channel) => {\n        if (channel.id === action.payload.channel) {\n          return {\n            ...channel,\n            solo: action.payload.solo,\n            muted: false,\n          };\n        }\n        return channel;\n      });\n    case CHANNELS_CONSTANTS.UPDATE_CHANNEL_ORDER:\n      return R.insert(\n        action.payload.newIndex,\n        state[action.payload.oldIndex],\n        R.remove(action.payload.oldIndex, 1, state),\n      );\n    case CHANNELS_CONSTANTS.SET_CHANNELS:\n      return [...action.payload];\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/channels/channels.reducer.test.js",
    "content": "import { channelsInitialState, channelsReducer } from './channels.reducer';\nimport {\n  setChannelSample,\n  setChannelGain,\n  setChannelPan,\n  addChannel,\n  removeChannel,\n  replaceChannels,\n  setChannelPitchCoarse,\n  setChannelPitchFine,\n  setChannelReverb,\n  setChannelMuted,\n  setChannelSolo,\n} from './channels.actions';\n\njest.mock('../../presets');\njest.mock('../../samples.config');\njest.mock('../../services/featureChecks');\n\nconst testSample = '/fake/sample/b/url.wav';\n\ndescribe('setChannelSample', () => {\n  test('should change a sample', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelSample(channelsInitialState[0].id, testSample),\n    );\n    expect(state[0].sample).toEqual(testSample);\n  });\n});\n\ndescribe('setChannelGain', () => {\n  test('should change gain for a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelGain(channelsInitialState[0].id, 0.5),\n    );\n    expect(state[0].gain).toEqual(0.5);\n  });\n});\n\ndescribe('setChannelPan', () => {\n  test('should change pan for a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelPan(channelsInitialState[0].id, 0.5),\n    );\n    expect(state[0].pan).toEqual(0.5);\n  });\n});\n\ndescribe('setChannelPitchCoarse', () => {\n  test('should change pitch (coarse) for a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelPitchCoarse(channelsInitialState[0].id, 5),\n    );\n    expect(state[0].pitchCoarse).toEqual(5);\n  });\n});\n\ndescribe('setChannelPitchFine', () => {\n  test('should change pitch (fine) for a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelPitchFine(channelsInitialState[0].id, -50),\n    );\n    expect(state[0].pitchFine).toEqual(-50);\n  });\n});\n\ndescribe('setChannelReverb', () => {\n  test('should change reverb for a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelReverb(channelsInitialState[0].id, 0.5),\n    );\n    expect(state[0].reverb).toEqual(0.5);\n  });\n});\n\ndescribe('setChannelMuted', () => {\n  test('should mute a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelMuted(channelsInitialState[0].id, true),\n    );\n    expect(state[0].muted).toEqual(true);\n  });\n\n  test('should set solo to false if it was true', () => {\n    const soloState = channelsReducer(\n      channelsInitialState,\n      setChannelSolo(channelsInitialState[0].id, true),\n    );\n    expect(soloState[0].solo).toEqual(true);\n    const state = channelsReducer(\n      soloState,\n      setChannelMuted(channelsInitialState[0].id, true),\n    );\n    expect(state[0].solo).toEqual(false);\n  });\n});\n\ndescribe('setChannelSolo', () => {\n  test('should solo a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      setChannelSolo(channelsInitialState[0].id, true),\n    );\n    expect(state[0].solo).toEqual(true);\n  });\n\n  test('should set muted to false if it was true', () => {\n    const mutedState = channelsReducer(\n      channelsInitialState,\n      setChannelMuted(channelsInitialState[0].id, true),\n    );\n    expect(mutedState[0].muted).toEqual(true);\n    const state = channelsReducer(\n      mutedState,\n      setChannelSolo(channelsInitialState[0].id, true),\n    );\n    expect(state[0].muted).toEqual(false);\n  });\n});\n\ndescribe('addChannel', () => {\n  test('should add a channel', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      addChannel({\n        id: '12345',\n        gain: 1,\n        sample: {},\n      }),\n    );\n    expect(state.length).toEqual(channelsInitialState.length + 1);\n  });\n});\n\ndescribe('removeChannel', () => {\n  test('should remove a channel that exists', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      removeChannel(channelsInitialState[0].id),\n    );\n    expect(state.length).toEqual(channelsInitialState.length - 1);\n  });\n\n  test('should do nothing if no channel matches the ID', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      removeChannel('foo'),\n    );\n    expect(state.length).toEqual(channelsInitialState.length);\n  });\n});\n\ndescribe('replaceChannels', () => {\n  test('should replace existing channels', () => {\n    const state = channelsReducer(\n      channelsInitialState,\n      replaceChannels([\n        {\n          id: 'empty_channel',\n          sample: 'test',\n          gain: 1,\n        },\n      ]),\n    );\n    expect(state.length).toEqual(1);\n  });\n});\n"
  },
  {
    "path": "src/common/channels/channels.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const channelsSelector = R.path(['channels']);\n"
  },
  {
    "path": "src/common/channels/index.js",
    "content": "export * from './channels.reducer';\nexport * from './channels.selectors';\nexport * from './channels.actions';\n"
  },
  {
    "path": "src/common/index.js",
    "content": "export * from './playbackSession';\nexport * from './channels';\nexport * from './tempo';\nexport * from './master';\nexport * from './notes';\nexport * from './presets';\nexport * from './window';\nexport * from './userSamples';\n"
  },
  {
    "path": "src/common/master/index.js",
    "content": "export * from './master.reducer';\nexport * from './master.selectors';\nexport * from './master.actions';\n"
  },
  {
    "path": "src/common/master/master.actions.js",
    "content": "import { MASTER_CONSTANTS } from './master.constants';\n\nexport const setPattern = (patternIndex) => ({\n  type: MASTER_CONSTANTS.SET_PATTERN,\n  payload: patternIndex,\n});\n\nexport const setSelectedChannel = (channelID) => ({\n  type: MASTER_CONSTANTS.SET_SELECTED_CHANNEL,\n  payload: channelID,\n});\n"
  },
  {
    "path": "src/common/master/master.constants.js",
    "content": "export const MASTER_CONSTANTS = {\n  SET_PATTERN: 'SET_PATTERN',\n  SET_SELECTED_CHANNEL: 'SET_SELECTED_CHANNEL',\n};\n"
  },
  {
    "path": "src/common/master/master.reducer.js",
    "content": "import { MASTER_CONSTANTS } from './master.constants';\nimport presets from '../../presets';\n\nexport const masterInitialState = {\n  pattern: 0,\n  selectedChannel: presets[1].channels[0].id,\n};\n\nexport const masterReducer = (state = masterInitialState, action) => {\n  switch (action.type) {\n    case MASTER_CONSTANTS.SET_PATTERN:\n      return {\n        ...state,\n        pattern: action.payload,\n      };\n    case MASTER_CONSTANTS.SET_SELECTED_CHANNEL:\n      return {\n        ...state,\n        selectedChannel: action.payload,\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/master/master.reducer.test.js",
    "content": "import { masterInitialState, masterReducer } from './master.reducer';\nimport {\n  setPattern,\n} from './master.actions';\n\ndescribe('setPattern', () => {\n  test('should change the pattern', () => {\n    const state = masterReducer(masterInitialState, setPattern(1));\n    expect(state.pattern).toEqual(1);\n  });\n});\n"
  },
  {
    "path": "src/common/master/master.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const patternSelector = R.path(['master', 'pattern']);\n\nexport const selectedChannelSelector = R.path(['master', 'selectedChannel']);\n"
  },
  {
    "path": "src/common/notes/index.js",
    "content": "export * from './notes.reducer';\nexport * from './notes.selectors';\nexport * from './notes.actions';\n"
  },
  {
    "path": "src/common/notes/notes.actions.js",
    "content": "import { NOTES_CONSTANTS } from './notes.constants';\n\nexport const initializeChannelNotes = (channelID) => ({\n  type: NOTES_CONSTANTS.INITIALIZE_CHANNEL,\n  payload: channelID,\n});\n\nexport const removeChannelNotes = (channelID) => ({\n  type: NOTES_CONSTANTS.REMOVE_CHANNEL,\n  payload: channelID,\n});\n\nexport const setNotes = (notes) => ({\n  type: NOTES_CONSTANTS.SET_NOTES,\n  payload: notes,\n});\n\nexport const toggleNote = (channelID, pattern, beat) => ({\n  type: NOTES_CONSTANTS.TOGGLE_NOTE,\n  payload: {\n    channelID,\n    pattern,\n    beat,\n  },\n});\n"
  },
  {
    "path": "src/common/notes/notes.constants.js",
    "content": "export const NOTES_CONSTANTS = {\n  INITIALIZE_CHANNEL: 'INITIALIZE_CHANNEL_NOTES',\n  REMOVE_CHANNEL: 'REMOVE_CHANNEL_NOTES',\n  TOGGLE_NOTE: 'TOGGLE_NOTE',\n  SET_NOTES: 'SET_NOTES',\n};\n"
  },
  {
    "path": "src/common/notes/notes.reducer.js",
    "content": "import { uuid } from '../../services/uuid';\nimport presets from '../../presets';\nimport { EMPTY_NOTE_ROW } from '../../presets/empty';\nimport { NOTES_CONSTANTS } from './notes.constants';\n\nexport const notesInitialState = presets[1].notes;\n\n// Returns a new noteAr clone with a note at beat either added or removed\nconst toggleNote = (noteAr, beat) => {\n  if (noteAr.find((note) => note.beat === beat)) {\n    return noteAr.filter((note) => note.beat !== beat);\n  }\n  return [\n    ...noteAr,\n    {\n      beat,\n      id: uuid(),\n    },\n  ];\n};\n\n// Returns new state object with note at beat on pattern toggled\nconst toggleNoteState = (state, { channelID, pattern, beat }) => ({\n  ...state,\n  [channelID]: state[channelID].map((noteAr, patternIndex) => {\n    if (patternIndex === pattern) {\n      // This is the active pattern\n      return toggleNote(noteAr, beat);\n    }\n    return noteAr; // Do nothing to other patterns\n  }),\n});\n\nexport const notesReducer = (state = notesInitialState, action) => {\n  switch (action.type) {\n    case NOTES_CONSTANTS.TOGGLE_NOTE:\n      return toggleNoteState(state, action.payload);\n    case NOTES_CONSTANTS.INITIALIZE_CHANNEL:\n      return {\n        ...state,\n        [action.payload]: EMPTY_NOTE_ROW, // TO DO: add empty array for each pattern\n      };\n    case NOTES_CONSTANTS.REMOVE_CHANNEL:\n      return {\n        ...state,\n        [action.payload]: undefined,\n      };\n    case NOTES_CONSTANTS.SET_NOTES:\n      return {\n        ...action.payload,\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/notes/notes.reducer.test.js",
    "content": "import { notesReducer } from './notes.reducer';\nimport {\n  toggleNote,\n  initializeChannelNotes,\n  removeChannelNotes,\n  setNotes,\n} from './notes.actions';\n\njest.mock('../../presets');\njest.mock('../../samples.config');\n\nconst testNotes = {\n  bongo: [\n    [\n      {\n        id: 'bing',\n        beat: 1,\n      },\n      {\n        id: 'bong',\n        beat: 3,\n      },\n    ],\n    [\n      {\n        id: 'ping',\n        beat: 2,\n      },\n      {\n        id: 'pang',\n        beat: 4,\n      },\n    ],\n  ],\n};\n\ndescribe('toggleNote', () => {\n  test('should toggle a note off', () => {\n    const state = notesReducer(testNotes, toggleNote('bongo', 0, 1));\n    expect(state.bongo[0].length).toBe(1);\n  });\n\n  test('should toggle a note on', () => {\n    const state = notesReducer(testNotes, toggleNote('bongo', 0, 2));\n    expect(state.bongo[0].length).toBe(3);\n  });\n\n  test('should toggle a note on the second preset on', () => {\n    const state = notesReducer(testNotes, toggleNote('bongo', 1, 1));\n    expect(state.bongo[1].length).toBe(3);\n  });\n});\n\ndescribe('initializeChannel', () => {\n  test('should add a channel', () => {\n    const state = notesReducer(\n      testNotes,\n      initializeChannelNotes('cowbell'),\n    );\n    expect(state.cowbell).not.toBeUndefined();\n  });\n});\n\ndescribe('removeChannel', () => {\n  test('should remove a channel that exists', () => {\n    const state = notesReducer(\n      testNotes,\n      removeChannelNotes('bongo'),\n    );\n    expect(state.bongo).toBeUndefined();\n  });\n\n  test('should do nothing if no channel matches the ID', () => {\n    const state = notesReducer(\n      testNotes,\n      removeChannelNotes('foobar'),\n    );\n    expect(state.bongo).not.toBeUndefined();\n    expect(state.foobar).toBeUndefined();\n  });\n});\n\ndescribe('setNotes', () => {\n  test('should replace existing channels', () => {\n    const state = notesReducer(\n      testNotes,\n      setNotes({\n        maracas: [\n          [],\n          [],\n        ],\n      }),\n    );\n    expect(state.bongo).toBeUndefined();\n    expect(state.maracas).not.toBeUndefined();\n  });\n});\n"
  },
  {
    "path": "src/common/notes/notes.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const notesSelector = R.path(['notes']);\n"
  },
  {
    "path": "src/common/playbackSession/index.js",
    "content": "export * from './playbackSession.actions';\nexport * from './playbackSession.reducer';\nexport * from './playbackSession.selectors';\n"
  },
  {
    "path": "src/common/playbackSession/playbackSession.actions.js",
    "content": "import { PLAYBACK_SESSION_CONSTANTS } from './playbackSession.constants';\nimport { getAudioContext } from '../../services/audioContext';\nimport { unmute } from '../../services/unmute';\n\nexport const startPlayback = () => ({\n  type: PLAYBACK_SESSION_CONSTANTS.START_PLAYBACK,\n});\n\nexport const stopPlayback = () => ({\n  type: PLAYBACK_SESSION_CONSTANTS.STOP_PLAYBACK,\n});\n\nexport const setStartTime = (val) => ({\n  type: PLAYBACK_SESSION_CONSTANTS.SET_START_TIME,\n  payload: val,\n});\n\nexport const startPlaybackAndResume = () => (dispatch) => {\n  unmute();\n  getAudioContext().resume();\n  dispatch(startPlayback());\n};\n"
  },
  {
    "path": "src/common/playbackSession/playbackSession.constants.js",
    "content": "export const PLAYBACK_SESSION_CONSTANTS = {\n  START_PLAYBACK: 'START_PLAYBACK',\n  STOP_PLAYBACK: 'STOP_PLAYBACK',\n  SET_START_TIME: 'SET_START_TIME',\n};\n"
  },
  {
    "path": "src/common/playbackSession/playbackSession.reducer.js",
    "content": "import { PLAYBACK_SESSION_CONSTANTS } from './playbackSession.constants';\nimport { getAudioContext } from '../../services/audioContext';\nimport { LOOKAHEAD } from '../../services/audioEngine.config';\n\nexport const playbackSessionInitialState = {\n  playing: false,\n  startTime: null,\n  currentBeat: 1,\n};\n\nexport const playbackSessionReducer = (state = playbackSessionInitialState, action) => {\n  switch (action.type) {\n    case PLAYBACK_SESSION_CONSTANTS.START_PLAYBACK:\n      return {\n        ...state,\n        playing: true,\n        startTime: getAudioContext().currentTime + LOOKAHEAD + LOOKAHEAD,\n      };\n    case PLAYBACK_SESSION_CONSTANTS.STOP_PLAYBACK:\n      return {\n        ...state,\n        playing: false,\n        startTime: null,\n      };\n    case PLAYBACK_SESSION_CONSTANTS.SET_START_TIME:\n      return {\n        ...state,\n        startTime: action.payload,\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/playbackSession/playbackSession.reducer.test.js",
    "content": "import {\n  playbackSessionInitialState,\n  playbackSessionReducer,\n} from './playbackSession.reducer';\nimport {\n  startPlayback,\n  stopPlayback,\n  setStartTime,\n} from './playbackSession.actions';\nimport { LOOKAHEAD } from '../../services/audioEngine.config';\n\njest.mock('../../services/audioContext.js');\n\ndescribe('startPlayback', () => {\n  test('should set playing to true', () => {\n    const state = playbackSessionReducer(playbackSessionInitialState, startPlayback());\n    expect(state.playing).toBe(true);\n  });\n\n  test('should set startTime to current time plus lookahead', () => {\n    const state = playbackSessionReducer(playbackSessionInitialState, startPlayback());\n    expect(state.startTime).toBe(1 + LOOKAHEAD + LOOKAHEAD);\n  });\n});\n\ndescribe('startPlayback', () => {\n  test('should set playing to false', () => {\n    const state = playbackSessionReducer(playbackSessionInitialState, stopPlayback());\n    expect(state.playing).toBe(false);\n  });\n\n  test('should set startTime to null', () => {\n    const state = playbackSessionReducer(playbackSessionInitialState, stopPlayback());\n    expect(state.startTime).toBeNull();\n  });\n});\n\ndescribe('setStartTime', () => {\n  test('should set startTime', () => {\n    const state = playbackSessionReducer(playbackSessionInitialState, setStartTime(2.1234));\n    expect(state.startTime).toBe(2.1234);\n  });\n});\n"
  },
  {
    "path": "src/common/playbackSession/playbackSession.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const playingSelector = R.path(['playbackSession', 'playing']);\nexport const startTimeSelector = R.path(['playbackSession', 'startTime']);\n"
  },
  {
    "path": "src/common/presets/index.js",
    "content": "export * from './presets.actions';\nexport * from './presets.reducer';\nexport * from './presets.selectors';\n"
  },
  {
    "path": "src/common/presets/presets.actions.js",
    "content": "import { setBPM, setSwing } from '../tempo';\nimport { loadChannels } from '../channels';\nimport { setPattern, setSelectedChannel } from '../master';\nimport { PRESETS_CONSTANTS } from './presets.constants';\nimport presets from '../../presets';\nimport { showFlashMessage, FLASH_MESSAGES } from '../window';\nimport { currentStateSelector } from './presets.selectors';\n\nexport const setPreset = (presetName) => ({\n  type: PRESETS_CONSTANTS.SET_PRESET,\n  payload: presetName,\n});\n\nexport const savePreset = (preset) => ({\n  type: PRESETS_CONSTANTS.SAVE_PRESET,\n  payload: preset,\n});\n\nexport const savePresetAs = (preset) => ({\n  type: PRESETS_CONSTANTS.SAVE_PRESET_AS,\n  payload: preset,\n});\n\nexport const deletePreset = (presetName) => ({\n  type: PRESETS_CONSTANTS.DELETE_PRESET,\n  payload: presetName,\n});\n\nexport const loadPreset = (preset) => (dispatch) => {\n  dispatch(setBPM(preset.bpm));\n  dispatch(setSwing(preset.swing));\n  dispatch(loadChannels(preset.channels, preset.notes));\n  dispatch(setPreset(preset.name));\n  dispatch(setPattern(0));\n  dispatch(setSelectedChannel(preset.channels[0].id));\n};\n\nexport const erasePreset = (presetName) => (dispatch) => {\n  dispatch(setBPM(presets[0].bpm));\n  dispatch(setSwing(presets[0].swing));\n  dispatch(loadChannels(presets[0].channels, presets[0].notes));\n  dispatch(setPreset(presets[0].name));\n  dispatch(setPattern(0));\n  dispatch(setSelectedChannel(presets[0].channels[0].id));\n  dispatch(deletePreset(presetName));\n  dispatch(showFlashMessage(FLASH_MESSAGES.PRESET_DELETED));\n};\n\nexport const doSavePresetAs = (presetName) => (dispatch, getState) => {\n  const currentState = currentStateSelector(getState());\n  dispatch(savePresetAs({\n    ...currentState,\n    name: presetName,\n  }));\n  dispatch(setPreset(presetName));\n  dispatch(showFlashMessage(FLASH_MESSAGES.PRESET_SAVED));\n};\n\nexport const doSavePreset = (presetName) => (dispatch, getState) => {\n  const currentState = currentStateSelector(getState());\n  dispatch(savePreset({\n    ...currentState,\n    name: presetName,\n  }));\n  dispatch(showFlashMessage(FLASH_MESSAGES.PRESET_SAVED));\n};\n"
  },
  {
    "path": "src/common/presets/presets.constants.js",
    "content": "export const PRESETS_CONSTANTS = {\n  SAVE_PRESET: 'SAVE_PRESET',\n  SAVE_PRESET_AS: 'SAVE_PRESET_AS',\n  DELETE_PRESET: 'DELETE_PRESET',\n  SET_PRESET: 'SET_PRESET',\n};\n"
  },
  {
    "path": "src/common/presets/presets.reducer.js",
    "content": "import { PRESETS_CONSTANTS } from './presets.constants';\nimport defaultPresets from '../../presets';\n\nexport const presetsInitialState = {\n  userPresets: [],\n  preset: defaultPresets[1].name,\n};\n\nexport const presetsReducer = (state = presetsInitialState, action) => {\n  switch (action.type) {\n    case PRESETS_CONSTANTS.SET_PRESET:\n      return {\n        ...state,\n        preset: action.payload,\n      };\n    case PRESETS_CONSTANTS.SAVE_PRESET:\n      return {\n        ...state,\n        userPresets: state.userPresets.map(\n          (userPreset) => (userPreset.name === action.payload.name\n            ? action.payload\n            : userPreset),\n        ),\n      };\n    case PRESETS_CONSTANTS.SAVE_PRESET_AS:\n      return {\n        ...state,\n        userPresets: [\n          ...state.userPresets.filter(\n            (userPreset) => userPreset.name !== action.payload.name,\n          ),\n          action.payload,\n        ],\n      };\n    case PRESETS_CONSTANTS.DELETE_PRESET:\n      return {\n        ...state,\n        userPresets: state.userPresets.filter(\n          (userPreset) => userPreset.name !== action.payload,\n        ),\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/presets/presets.reducer.test.js",
    "content": "import { presetsInitialState, presetsReducer } from './presets.reducer';\nimport {\n  setPreset,\n  savePreset,\n  savePresetAs,\n  deletePreset,\n} from './presets.actions';\n\njest.mock('../../presets');\njest.mock('../../services/featureChecks');\n\nconst testPreset = {\n  name: 'Test preset',\n  bpm: 120,\n};\n\ndescribe('setPreset', () => {\n  test('should change the preset', () => {\n    const state = presetsReducer(presetsInitialState, setPreset('hello'));\n    expect(state.preset).toEqual('hello');\n  });\n});\n\ndescribe('savePresetAs', () => {\n  test('should add a new user preset', () => {\n    const state = presetsReducer(presetsInitialState, savePresetAs(testPreset));\n    expect(state.userPresets.length).toEqual(1);\n  });\n});\n\ndescribe('savePreset', () => {\n  test('should update a user preset', () => {\n    const state = presetsReducer(presetsInitialState, savePresetAs(testPreset));\n    expect(state.userPresets.length).toEqual(1);\n    expect(state.userPresets[0].bpm).toEqual(120);\n    const newState = presetsReducer(state, savePreset({\n      name: 'Test preset',\n      bpm: 100,\n    }));\n    expect(newState.userPresets.length).toEqual(1);\n    expect(newState.userPresets[0].bpm).toEqual(100);\n  });\n});\n\ndescribe('deletePreset', () => {\n  test('should add a new user preset', () => {\n    const state = presetsReducer(presetsInitialState, savePresetAs(testPreset));\n    expect(state.userPresets.length).toEqual(1);\n    const newState = presetsReducer(state, deletePreset('Test preset'));\n    expect(newState.userPresets.length).toEqual(0);\n  });\n});\n"
  },
  {
    "path": "src/common/presets/presets.selectors.js",
    "content": "import * as R from 'ramda';\nimport { createSelector } from 'reselect';\nimport { channelsSelector } from '../channels';\nimport { notesSelector } from '../notes';\nimport { bpmSelector, swingSelector } from '../tempo';\n\nexport const userPresetsSelector = R.path(['presets', 'userPresets']);\n\nexport const presetSelector = R.path(['presets', 'preset']);\n\nexport const currentStateSelector = createSelector(\n  channelsSelector,\n  notesSelector,\n  bpmSelector,\n  swingSelector,\n  (channels, notes, bpm, swing) => ({\n    notes,\n    bpm,\n    swing,\n    channels: channels.map(\n      (channel) => R.omit(['sampleLoaded'], channel),\n    ),\n  }),\n);\n"
  },
  {
    "path": "src/common/tempo/index.js",
    "content": "export * from './tempo.actions';\nexport * from './tempo.reducer';\nexport * from './tempo.selectors';\n"
  },
  {
    "path": "src/common/tempo/tempo.actions.js",
    "content": "import { TEMPO_CONSTANTS } from './tempo.constants';\n\nexport const setBPM = (val) => ({\n  type: TEMPO_CONSTANTS.SET_BPM,\n  payload: val,\n});\n\nexport const setSwing = (val) => ({\n  type: TEMPO_CONSTANTS.SET_SWING,\n  payload: val,\n});\n"
  },
  {
    "path": "src/common/tempo/tempo.constants.js",
    "content": "export const TEMPO_CONSTANTS = {\n  SET_BPM: 'SET_BPM',\n  SET_SWING: 'SET_SWING',\n};\n"
  },
  {
    "path": "src/common/tempo/tempo.reducer.js",
    "content": "import { TEMPO_CONSTANTS } from './tempo.constants';\nimport presets from '../../presets';\n\nexport const tempoInitialState = {\n  bpm: presets[1].bpm,\n  swing: presets[1].swing,\n};\n\nexport const tempoReducer = (state = tempoInitialState, action) => {\n  switch (action.type) {\n    case TEMPO_CONSTANTS.SET_BPM:\n      return {\n        ...state,\n        bpm: action.payload,\n      };\n    case TEMPO_CONSTANTS.SET_SWING:\n      return {\n        ...state,\n        swing: action.payload,\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/tempo/tempo.reducer.test.js",
    "content": "import {\n  tempoInitialState,\n  tempoReducer,\n} from './tempo.reducer';\nimport { setBPM, setSwing } from './tempo.actions';\n\njest.mock('../../presets');\n\ndescribe('setBPM', () => {\n  test('should set bpm', () => {\n    const state = tempoReducer(tempoInitialState, setBPM(123));\n    expect(state.bpm).toBe(123);\n  });\n});\n\ndescribe('setSwing', () => {\n  test('should set swing', () => {\n    const state = tempoReducer(tempoInitialState, setSwing(0.4));\n    expect(state.swing).toBe(0.4);\n  });\n});\n"
  },
  {
    "path": "src/common/tempo/tempo.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const bpmSelector = R.path(['tempo', 'bpm']);\nexport const swingSelector = R.path(['tempo', 'swing']);\n"
  },
  {
    "path": "src/common/userSamples/index.js",
    "content": "export * from './userSamples.actions';\nexport * from './userSamples.reducer';\nexport * from './userSamples.selectors';\n"
  },
  {
    "path": "src/common/userSamples/userSamples.actions.js",
    "content": "import { saveToSampleStore } from '../../services/sampleStore';\nimport { USER_SAMPLES_CONSTANTS } from './userSamples.constants';\nimport { loadAndSetChannelSample } from '../channels';\nimport { showFlashMessage, FLASH_MESSAGES } from '../window';\n\nexport const addUserSample = (sample) => ({\n  type: USER_SAMPLES_CONSTANTS.ADD_USER_SAMPLE,\n  payload: sample,\n});\n\nexport const removeUserSample = (sampleId) => ({\n  type: USER_SAMPLES_CONSTANTS.REMOVE_USER_SAMPLE,\n  payload: sampleId,\n});\n\nexport const clearUserSamples = () => ({\n  type: USER_SAMPLES_CONSTANTS.CLEAR_USER_SAMPLES,\n});\n\nexport const saveUserSample = (channel, files) => (dispatch) => {\n  saveToSampleStore(files[0])\n    .then((sampleURL) => {\n      dispatch(addUserSample(sampleURL));\n      dispatch(loadAndSetChannelSample(channel, sampleURL));\n    })\n    .catch(() => {\n      dispatch(showFlashMessage(FLASH_MESSAGES.SAMPLE_LOAD_ERROR));\n    });\n};\n"
  },
  {
    "path": "src/common/userSamples/userSamples.constants.js",
    "content": "export const USER_SAMPLES_CONSTANTS = {\n  ADD_USER_SAMPLE: 'ADD_USER_SAMPLE',\n  REMOVE_USER_SAMPLE: 'REMOVE_USER_SAMPLE',\n  CLEAR_USER_SAMPLES: 'CLEAR_USER_SAMPLES',\n};\n"
  },
  {
    "path": "src/common/userSamples/userSamples.reducer.js",
    "content": "import { USER_SAMPLES_CONSTANTS } from './userSamples.constants';\n\nexport const userSamplesInitialState = [];\n\nexport const userSamplesReducer = (state = userSamplesInitialState, action) => {\n  switch (action.type) {\n    case USER_SAMPLES_CONSTANTS.ADD_USER_SAMPLE:\n      return [\n        ...state,\n        action.payload,\n      ];\n    case USER_SAMPLES_CONSTANTS.REMOVE_USER_SAMPLE:\n      return state.filter((userSample) => userSample.id !== action.payload);\n    case USER_SAMPLES_CONSTANTS.CLEAR_USER_SAMPLES:\n      return userSamplesInitialState;\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/userSamples/userSamples.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const userSamplesSelector = R.path(['userSamples']);\n"
  },
  {
    "path": "src/common/window/index.js",
    "content": "export * from './window.actions';\nexport * from './window.reducer';\nexport * from './window.selectors';\nexport * from './window.constants';\n"
  },
  {
    "path": "src/common/window/window.actions.js",
    "content": "import { WINDOW_CONSTANTS } from './window.constants';\n\nexport const setPresetPrompt = (isOpen) => ({\n  type: WINDOW_CONSTANTS.PRESET_PROMPT_OPEN,\n  payload: isOpen,\n});\n\nexport const setPresetNameField = (val) => ({\n  type: WINDOW_CONSTANTS.SET_PRESET_NAME_FIELD,\n  payload: val,\n});\n\nexport const showFlashMessage = (messageKey) => ({\n  type: WINDOW_CONSTANTS.SET_FLASH_MESSAGE,\n  payload: messageKey,\n});\n\nexport const clearFlashMessage = () => ({\n  type: WINDOW_CONSTANTS.CLEAR_FLASH_MESSAGE,\n});\n\nexport const setCanInstall = (canInstall) => ({\n  type: WINDOW_CONSTANTS.SET_CAN_INSTALL,\n  payload: canInstall,\n});\n"
  },
  {
    "path": "src/common/window/window.constants.js",
    "content": "export const WINDOW_CONSTANTS = {\n  PRESET_PROMPT_OPEN: 'PRESET_PROMPT_OPEN',\n  SET_PRESET_NAME_FIELD: 'SET_PRESET_NAME_FIELD',\n  SET_FLASH_MESSAGE: 'SET_FLASH_MESSAGE',\n  CLEAR_FLASH_MESSAGE: 'CLEAR_FLASH_MESSAGE',\n  SET_CAN_INSTALL: 'SET_CAN_INSTALL',\n};\n\nexport const FLASH_MESSAGES = {\n  INSTALL_PWA: 'FLASH_MESSAGE_INSTALL_PWA',\n  SAMPLE_LOAD_ERROR: 'SAMPLE_LOAD_ERROR',\n  PRESET_SAVED: 'PRESET_SAVED',\n  PRESET_DELETED: 'PRESET_DELETED',\n};\n"
  },
  {
    "path": "src/common/window/window.reducer.js",
    "content": "import { WINDOW_CONSTANTS } from './window.constants';\n\nexport const windowInitialState = {\n  presetPromptOpen: false,\n  flashMessageKey: null,\n  flashMessageVisible: false,\n  canInstall: false,\n};\n\nexport const windowReducer = (state = windowInitialState, action) => {\n  switch (action.type) {\n    case WINDOW_CONSTANTS.PRESET_PROMPT_OPEN:\n      return {\n        ...state,\n        presetPromptOpen: action.payload,\n      };\n    case WINDOW_CONSTANTS.SET_FLASH_MESSAGE:\n      return {\n        ...state,\n        flashMessageKey: action.payload,\n        flashMessageVisible: true,\n      };\n    case WINDOW_CONSTANTS.CLEAR_FLASH_MESSAGE:\n      return {\n        ...state,\n        flashMessageVisible: false,\n      };\n    case WINDOW_CONSTANTS.SET_CAN_INSTALL:\n      return {\n        ...state,\n        canInstall: action.payload,\n      };\n    default:\n      return state;\n  }\n};\n"
  },
  {
    "path": "src/common/window/window.reducer.test.js",
    "content": "import { windowInitialState, windowReducer } from './window.reducer';\nimport {\n  setPresetPrompt,\n  showFlashMessage,\n  clearFlashMessage,\n  setCanInstall,\n} from './window.actions';\n\ndescribe('setPresetPrompt', () => {\n  test('should set presetPromptOpen to true', () => {\n    const state = windowReducer(windowInitialState, setPresetPrompt(true));\n    expect(state.presetPromptOpen).toBe(true);\n  });\n});\n\ndescribe('showFlashMessage', () => {\n  test('should set flashMessageKey to a string value', () => {\n    const state = windowReducer(windowInitialState, showFlashMessage('foobar'));\n    expect(state.flashMessageKey).toBe('foobar');\n    expect(state.flashMessageVisible).toEqual(true);\n  });\n});\n\ndescribe('clearFlashMessage', () => {\n  test('should set flashMessageKey to null', () => {\n    const state = windowReducer(windowInitialState, showFlashMessage('foobar'));\n    const nullState = windowReducer(state, clearFlashMessage());\n    expect(nullState.flashMessageKey).toBe('foobar');\n    expect(nullState.flashMessageVisible).toEqual(false);\n  });\n});\n\ndescribe('setCanInstall', () => {\n  test('should set canInstall to a value', () => {\n    const state = windowReducer(windowInitialState, setCanInstall(true));\n    expect(state.canInstall).toBe(true);\n  });\n});\n"
  },
  {
    "path": "src/common/window/window.selectors.js",
    "content": "import * as R from 'ramda';\n\nexport const presetPromptOpenSelector = R.path(['window', 'presetPromptOpen']);\nexport const flashMessageKeySelector = R.path(['window', 'flashMessageKey']);\nexport const flashMessageVisibleSelector = R.path(['window', 'flashMessageVisible']);\nexport const canInstallSelector = R.path(['window', 'canInstall']);\n"
  },
  {
    "path": "src/components/AddChannelButton/AddChannelButton.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { HoverButton } from '../design-system';\n\nexport const AddChannelButtonComponent = ({ newChannel }) => (\n  <HoverButton\n    onClick={newChannel}\n    width=\"16rem\"\n    bg=\"darkGray\"\n    color=\"gray\"\n    hoverColor=\"nearWhite\"\n    hoverBg=\"steel\"\n    transitionSpeed=\"0.2s\"\n    ml={1}\n    mt={2}\n    py=\"1rem\"\n  >\n    Add Channel +\n  </HoverButton>\n);\n\nAddChannelButtonComponent.propTypes = {\n  newChannel: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/AddChannelButton/AddChannelButton.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose } from 'recompose';\nimport { AddChannelButtonComponent } from './AddChannelButton.component';\nimport { newChannel } from '../../common';\n\nconst mapDispatchToProps = {\n  newChannel,\n};\n\nexport const AddChannelButton = compose(\n  connect(null, mapDispatchToProps),\n)(AddChannelButtonComponent);\n"
  },
  {
    "path": "src/components/AddChannelButton/index.js",
    "content": "export * from './AddChannelButton.container';\n"
  },
  {
    "path": "src/components/App.jsx",
    "content": "import React from 'react';\nimport { ThemeProvider } from 'styled-components';\nimport theme from '../styles/theme';\nimport globalStyles from '../styles/globalStyles';\nimport {\n  Box,\n  ChannelList,\n  ChannelHeader,\n  ChannelControls,\n  MasterControls,\n  Branding,\n  GithubLink,\n  FlashMessage,\n  InstallButton,\n} from '.';\n\nglobalStyles();\n\nconst App = () => (\n  <ThemeProvider theme={theme}>\n    <Box\n      className=\"App\"\n      position=\"relative\"\n      p={[0, 0, 0, 1, 2, 2, 2, 3, 5]}\n      pt={[4, 4, 4, 4, 4, 4, 4, 4, 5]}\n    >\n      <header>\n        <Box\n          display=\"flex\"\n          justifyContent=\"space-between\"\n          alignItems=\"flex-start\"\n        >\n          <Branding />\n          <InstallButton />\n        </Box>\n      </header>\n      <main>\n        <Box position=\"sticky\" bg=\"nearBlack\" top=\"0\" zIndex=\"10\" pt={2}>\n          <MasterControls />\n          <ChannelControls />\n          <ChannelHeader />\n        </Box>\n        <ChannelList />\n      </main>\n      <footer>\n        <Box\n          position=\"absolute\"\n          bottom={0}\n          right={0}\n          mr={[0, 0, 0, 1, 2, 2, 2, 3, 5]}\n        >\n          <GithubLink />\n        </Box>\n      </footer>\n      <FlashMessage />\n    </Box>\n  </ThemeProvider>\n);\n\nexport default App;\n"
  },
  {
    "path": "src/components/BPMInput/BPMInput.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport theme from '../../styles/theme';\nimport {\n  Box,\n  Label,\n  TextInput,\n  Button,\n} from '../design-system';\n\nconst ShinyBox = Box.extend`\n  background: linear-gradient(190deg, #19191D 0%, #303036 50%,#0a0e0a 51%, #29292D 100%);\n  transition: border-color 0.2s;\n\n  &:hover {\n    border-color: ${theme.colors.gray};\n  }\n`;\n\nconst BPMButton = Button.extend`\n  &:active {\n    background-color: rgba(255, 255, 255, 0.2);\n  }\n`;\n\nexport const BPMInputComponent = ({ bpm, setBPM }) => (\n  <ShinyBox\n    display=\"flex\"\n    border=\"2px solid\"\n    borderColor=\"steel\"\n    borderRadius=\"0.5rem\"\n    position=\"relative\"\n  >\n    <Label\n      position=\"absolute\"\n      left=\"0.5rem\"\n      top=\"-0.6em\"\n      color=\"gray\"\n      fontSize=\"0.6rem\"\n      fontWeight=\"600\"\n      bg=\"nearBlack\"\n      pl={1}\n      pr={1}\n      borderRadius=\"3px\"\n      letterSpacing=\"0.1em\"\n      htmlFor=\"bpm\"\n    >\n      BPM\n    </Label>\n    <TextInput\n      min=\"1\"\n      max=\"999\"\n      flex=\"1 1 auto\"\n      position=\"relative\"\n      zIndex={1}\n      width=\"3rem\"\n      height=\"100%\"\n      pl=\"0.7rem\"\n      fontSize=\"1.5rem\"\n      lineHeight=\"1em\"\n      fontWeight=\"500\"\n      bg=\"transparent\"\n      id=\"bpm\"\n      color=\"brightRed\"\n      className=\"bpm-text-input\"\n      type=\"number\"\n      value={bpm}\n      onChange={(e) => {\n        setBPM(parseInt(e.target.value, 10));\n      }}\n    />\n    <Box display=\"flex\" flexDirection=\"column\">\n      <BPMButton\n        color=\"white\"\n        bg=\"transparent\"\n        p={0}\n        flex=\"auto\"\n        width=\"2rem\"\n        borderRadius=\"0 0.5rem 0 0\"\n        onClick={() => {\n          setBPM(bpm + 1);\n        }}\n        aria-label=\"Increase beat per minute\"\n      >\n        <svg width=\"10px\" height=\"6px\" viewBox=\"0 0 12 7\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xlink=\"http://www.w3.org/1999/xlink\">\n          <polygon points=\"0 7 12 7 6 0\" fill=\"white\" />\n        </svg>\n      </BPMButton>\n      <BPMButton\n        color=\"white\"\n        bg=\"transparent\"\n        p={0}\n        flex=\"auto\"\n        width=\"2rem\"\n        borderRadius=\"0 0 0.5rem 0\"\n        onClick={() => {\n          setBPM(bpm - 1);\n        }}\n        aria-label=\"Decrease beat per minute\"\n      >\n        <svg width=\"10px\" height=\"6px\" viewBox=\"0 0 12 7\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xlink=\"http://www.w3.org/1999/xlink\">\n          <polygon points=\"0 0 12 0 6 7\" fill=\"white\" />\n        </svg>\n      </BPMButton>\n    </Box>\n  </ShinyBox>\n);\n\nBPMInputComponent.propTypes = {\n  bpm: PropTypes.number.isRequired,\n  setBPM: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/BPMInput/BPMInput.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose } from 'recompose';\nimport { BPMInputComponent } from './BPMInput.component';\nimport { bpmInputSelectors } from './BPMInput.selectors';\nimport { setBPM } from '../../common';\n\nconst mapDispatchToProps = {\n  setBPM,\n};\n\nexport const BPMInput = compose(\n  connect(bpmInputSelectors, mapDispatchToProps),\n)(BPMInputComponent);\n"
  },
  {
    "path": "src/components/BPMInput/BPMInput.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { bpmSelector } from '../../common';\n\nexport const bpmInputSelectors = createStructuredSelector({\n  bpm: bpmSelector,\n});\n"
  },
  {
    "path": "src/components/BPMInput/index.js",
    "content": "export * from './BPMInput.container';\n"
  },
  {
    "path": "src/components/Branding.jsx",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport theme from '../styles/theme';\nimport { Logo } from './Logo.component';\nimport { Box } from './design-system';\n\nconst HeaderText = styled.h1`\n  color: ${theme.colors.steel};\n  font-size: 1em;\n  font-weight: 600;\n  margin-left: 1.5em;\n  line-height: 1.2em;\n  margin-top: 0.5em;\n  max-width: 4em;\n`;\n\nexport const Branding = () => (\n  <Box mb={4} display=\"flex\" alignItems=\"center\">\n    <Logo color=\"white\" width=\"200px\" />\n    <HeaderText color=\"white\">\n      Web Drum Sequencer\n    </HeaderText>\n  </Box>\n);\n"
  },
  {
    "path": "src/components/Channel/Channel.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport * as R from 'ramda';\nimport { Toggles } from '../Toggles';\nimport {\n  Box,\n  Text,\n  Image,\n} from '../design-system';\nimport { RemoveButton } from './RemoveButton.component';\nimport { HitButton } from './HitButton.component';\nimport { MuteSolo } from '../MuteSolo';\nimport construction from '../../assets/images/construction-light.svg';\nimport samples from '../../samples.config';\n\nconst getSampleName = (sampleURL) => {\n  const maybeName = R.find(R.propEq('url', sampleURL))(samples);\n  return maybeName ? maybeName.name : sampleURL;\n};\n\nconst ChannelBox = Box.extend`\n  outline: none;\n  \n  &.draggable-source--is-dragging {\n    opacity: 0.2;\n  }\n\n  &.draggable-mirror {\n    opacity: 0.9;\n    z-index: 10;\n  }\n`;\n\nconst MoveImage = Image.extend`\n  cursor: move;\n  opacity: 0.2;\n  transition: opacity 0.1s;\n\n  &:hover, &:focus, &:active {\n    opacity: 0.3;\n  }\n`;\n\nexport const ChannelComponent = ({\n  channel,\n  onPressRemove,\n  notes,\n  pattern,\n  onPressHitButton,\n  onTouchChannel,\n  selectedChannelId,\n}) => {\n  const sampleName = getSampleName(channel.sample);\n  return (\n    <ChannelBox\n      width=\"100%\"\n      display=\"flex\"\n      flex=\"1 1 auto\"\n      p={1}\n      alignItems=\"stretch\"\n      borderRadius={0}\n      onMouseDown={onTouchChannel}\n      bg={selectedChannelId === channel.id ? 'darkGray' : 'transparent'}\n      outline=\"none\"\n      className=\"wds-draggable\"\n    >\n      <Box\n        bg={selectedChannelId === channel.id ? 'steel' : 'darkGray'}\n        width=\"16rem\"\n        p={2}\n        borderRadius=\"0.25rem\"\n        mr={[2, 2, 2, 3, 4]}\n        display=\"flex\"\n        alignItems=\"center\"\n        position=\"relative\"\n      >\n        <MoveImage src={construction} height=\"2.5rem\" mr={3} userSelect=\"none\" className=\"wds-channel-handle\" />\n        <Box flex=\"1 1 auto\">\n          <Text color=\"white\" fontWeight=\"normal\" textAlign=\"left\" fontSize={2} userSelect=\"none\">\n            {sampleName}\n          </Text>\n        </Box>\n        <MuteSolo channel={channel} />\n        <HitButton channel={channel} onMouseDown={onPressHitButton} />\n      </Box>\n      <Toggles\n        notes={notes[channel.id][pattern]}\n        channelID={channel.id}\n      />\n      <RemoveButton onClick={onPressRemove} />\n    </ChannelBox>\n  );\n};\n\nChannelComponent.propTypes = {\n  notes: PropTypes.objectOf(PropTypes.array).isRequired,\n  channel: PropTypes.shape({\n    sample: PropTypes.string,\n    id: PropTypes.string.isRequired,\n  }).isRequired,\n  onPressRemove: PropTypes.func.isRequired,\n  pattern: PropTypes.number.isRequired,\n  onPressHitButton: PropTypes.func.isRequired,\n  onTouchChannel: PropTypes.func.isRequired,\n  selectedChannelId: PropTypes.string.isRequired,\n};\n"
  },
  {
    "path": "src/components/Channel/Channel.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { ChannelComponent } from './Channel.component';\nimport { channelSelectors } from './Channel.selectors';\nimport {\n  deleteChannel,\n  setSelectedChannel,\n} from '../../common';\nimport { playNoteNow } from '../../services/audioScheduler';\n\nconst mapDispatchToProps = {\n  deleteChannel,\n  setSelectedChannel,\n};\n\nconst handlers = withHandlers({\n  onSelectSample: (props) => (sample) => {\n    const { loadAndSetChannelSample: scs, channel } = props;\n    scs(channel.id, sample.value);\n  },\n  onTouchChannel: (props) => () => {\n    const { channel, setSelectedChannel: sscs } = props;\n    sscs(channel.id);\n  },\n  onPressRemove: (props) => () => {\n    const {\n      channel,\n      channels,\n      selectedChannelId,\n      deleteChannel: dc,\n    } = props;\n    dc(channel.id, channels, selectedChannelId);\n  },\n  onPressHitButton: (props) => () => {\n    const { channel } = props;\n    playNoteNow(channel);\n  },\n});\n\nexport const Channel = compose(\n  connect(channelSelectors, mapDispatchToProps),\n  handlers,\n)(ChannelComponent);\n"
  },
  {
    "path": "src/components/Channel/Channel.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport {\n  channelsSelector,\n  notesSelector,\n  patternSelector,\n  selectedChannelSelector,\n} from '../../common';\n\nexport const channelSelectors = createStructuredSelector({\n  channels: channelsSelector,\n  notes: notesSelector,\n  pattern: patternSelector,\n  selectedChannelId: selectedChannelSelector,\n});\n"
  },
  {
    "path": "src/components/Channel/HitButton.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { HoverButton } from '../design-system';\n\nexport const HitButton = ({ channel, onMouseDown }) => (\n  <HoverButton\n    height={40}\n    width={40}\n    minWidth={40}\n    bg=\"lightGray\"\n    activeBg=\"gray\"\n    transitionSpeed=\"0.1s\"\n    ml={2}\n    p={0}\n    onMouseDown={onMouseDown}\n    onTouchStart={onMouseDown}\n    onTouchEnd={e => e.preventDefault()}\n    aria-label={`Play ${channel.sample.name}`}\n    touch-action=\"manipulation\"\n  />\n);\n\nHitButton.propTypes = {\n  channel: PropTypes.shape({\n    sample: PropTypes.string.isRequired,\n    gain: PropTypes.number.isRequired,\n  }).isRequired,\n  onMouseDown: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/Channel/RemoveButton.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { HoverButton } from '../design-system';\nimport theme from '../../styles/theme';\n\nexport const RemoveButton = ({ onClick }) => (\n  <HoverButton\n    bg=\"transparent\"\n    hoverBg=\"darkGray\"\n    color=\"gray\"\n    hoverColor=\"nearWhite\"\n    p={0}\n    borderRadius=\"3em\"\n    width={[20, 20, 30, 30, 40]}\n    height={[20, 20, 30, 30, 40]}\n    display=\"flex\"\n    justifyContent=\"center\"\n    onClick={onClick}\n    fontSize={3}\n    alignItems=\"middle\"\n    alignSelf=\"center\"\n    transitionSpeed=\"0.2s\"\n    aria-label=\"Remove channel\"\n  >\n    <svg width=\"14\" height=\"14\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\">\n      <path d=\"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z\" fill={theme.colors.steel} />\n    </svg>\n  </HoverButton>\n);\n\nRemoveButton.propTypes = {\n  onClick: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/Channel/index.js",
    "content": "export * from './Channel.container';\n"
  },
  {
    "path": "src/components/ChannelControls/ChannelControls.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  detuneSupported,\n} from '../../services/featureChecks';\nimport { Box } from '../design-system';\nimport { SampleSelect } from '../SampleSelect';\nimport { InfoKnob } from '../InfoKnob.component';\nimport { LabelBox } from '../LabelBox';\n\nconst ControlCluster = Box.extend`\n  background-color: ${({ theme }) => theme.colors.darkGray};\n  border-radius: 0.3rem;\n  display: flex;\n  margin: 0.5rem;\n  align-items: flex-start;\n  padding: 0.8rem;\n`;\n\nexport const ChannelControlsComponent = ({\n  channel,\n  onSetGain,\n  onSetPan,\n  onSetChannelPitchCoarse,\n  onSetReverb,\n}) => (\n  <LabelBox label=\"CHANNEL CONTROL\">\n    <ControlCluster>\n      <Box width=\"17rem\" pr={2}>\n        <SampleSelect channel={channel} />\n      </Box>\n      <Box>\n        {detuneSupported && (\n          <InfoKnob\n            label=\"PITCH\"\n            minLabel=\"-24\"\n            maxLabel=\"24\"\n            min=\"-24\"\n            max=\"24\"\n            value={channel.pitchCoarse || 0}\n            onChange={onSetChannelPitchCoarse}\n          />\n        )}\n      </Box>\n    </ControlCluster>\n    <ControlCluster>\n      <Box mr={4}>\n        <InfoKnob\n          label=\"PAN\"\n          minLabel=\"L\"\n          maxLabel=\"R\"\n          min=\"-1\"\n          max=\"1\"\n          step=\"0.1\"\n          value={channel.pan || 0}\n          onChange={onSetPan}\n        />\n      </Box>\n      <Box>\n        <InfoKnob\n          label=\"VOL\"\n          minLabel=\"0\"\n          maxLabel=\"1\"\n          value={channel.gain * 100}\n          onChange={onSetGain}\n        />\n      </Box>\n    </ControlCluster>\n    <ControlCluster>\n      <Box>\n        <InfoKnob\n          label=\"REVERB\"\n          minLabel=\"0\"\n          maxLabel=\"1\"\n          min=\"0\"\n          max=\"1\"\n          step=\"0.01\"\n          value={channel.reverb || 0}\n          onChange={onSetReverb}\n        />\n      </Box>\n    </ControlCluster>\n  </LabelBox>\n);\n\nChannelControlsComponent.propTypes = {\n  channel: PropTypes.shape({\n    id: PropTypes.string.isRequired,\n    pitchCoarse: PropTypes.number,\n    reverb: PropTypes.number,\n    gain: PropTypes.number,\n    pan: PropTypes.number,\n  }).isRequired,\n  onSetGain: PropTypes.func.isRequired,\n  onSetPan: PropTypes.func.isRequired,\n  onSetChannelPitchCoarse: PropTypes.func.isRequired,\n  onSetReverb: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/ChannelControls/ChannelControls.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { ChannelControlsComponent } from './ChannelControls.component';\nimport { channelControlsSelectors } from './ChannelControls.selectors';\nimport {\n  setChannelGain,\n  setChannelPitchCoarse,\n  setChannelPan,\n  setChannelReverb,\n} from '../../common';\n\nconst mapDispatchToProps = {\n  setChannelGain,\n  setChannelPan,\n  setChannelPitchCoarse,\n  setChannelReverb,\n};\n\nconst handlers = withHandlers({\n  onSetGain: (props) => (e) => {\n    const { setChannelGain: setChannelGainConnected, channel } = props;\n    setChannelGainConnected(channel.id, e.target.value / 100);\n  },\n  onSetPan: (props) => (e) => {\n    const { setChannelPan: setChannelPanConnected, channel } = props;\n    setChannelPanConnected(channel.id, e.target.value);\n  },\n  onSetChannelPitchCoarse: (props) => (e) => {\n    const { setChannelPitchCoarse: setChannelPitchCoarseConnected, channel } = props;\n    setChannelPitchCoarseConnected(channel.id, e.target.value);\n  },\n  onSetReverb: (props) => (e) => {\n    const { setChannelReverb: setChannelReverbConnected, channel } = props;\n    setChannelReverbConnected(channel.id, e.target.value);\n  },\n});\n\nexport const ChannelControls = compose(\n  connect(channelControlsSelectors, mapDispatchToProps),\n  handlers,\n)(ChannelControlsComponent);\n"
  },
  {
    "path": "src/components/ChannelControls/ChannelControls.selectors.js",
    "content": "import { createStructuredSelector, createSelector } from 'reselect';\nimport { channelsSelector, selectedChannelSelector } from '../../common';\n\nconst channelSelector = createSelector(\n  channelsSelector,\n  selectedChannelSelector,\n  (channels, selectedChannelID) => {\n    const selectedChannel = channels.find(\n      (channel) => channel.id === selectedChannelID,\n    );\n    return typeof selectedChannel === 'undefined' ? channels[0] : selectedChannel;\n  },\n);\n\nexport const channelControlsSelectors = createStructuredSelector({\n  channel: channelSelector,\n});\n"
  },
  {
    "path": "src/components/ChannelControls/index.js",
    "content": "export * from './ChannelControls.container';\n"
  },
  {
    "path": "src/components/ChannelHeader/ChannelHeader.component.jsx",
    "content": "import React from 'react';\nimport { Box } from '../design-system';\nimport { ChannelHeaderLabel } from './ChannelHeaderLabel.component';\nimport { Marker } from '../Marker';\n\nexport const ChannelHeader = () => (\n  <Box bg=\"black\" display=\"flex\" mt={3} pr={40}>\n    <Box width=\"16rem\" display=\"flex\" mr={[2, 2, 2, 3, 4]}>\n      <ChannelHeaderLabel flex=\"1 1 auto\" mr={[2]}>\n        Channels\n      </ChannelHeaderLabel>\n      <ChannelHeaderLabel width={30} mr={2} centerText>\n        Hit\n      </ChannelHeaderLabel>\n    </Box>\n    <Marker>\n      <ChannelHeaderLabel flex=\"1 1 auto\">\n        1\n      </ChannelHeaderLabel>\n      <ChannelHeaderLabel flex=\"1 1 auto\">\n        2\n      </ChannelHeaderLabel>\n      <ChannelHeaderLabel flex=\"1 1 auto\">\n        3\n      </ChannelHeaderLabel>\n      <ChannelHeaderLabel flex=\"1 1 auto\">\n        4\n      </ChannelHeaderLabel>\n    </Marker>\n  </Box>\n);\n"
  },
  {
    "path": "src/components/ChannelHeader/ChannelHeaderLabel.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Box, Text } from '../design-system';\n\nconst HeaderText = Text.extend`\n  text-transform: uppercase;\n`;\n\nexport const ChannelHeaderLabel = ({ children, centerText, ...restProps }) => (\n  <Box p={1} {...restProps}>\n    <HeaderText\n      color=\"white\"\n      fontWeight=\"700\"\n      fontSize=\"0.7rem\"\n      textAlign={centerText ? 'center' : 'left'}\n    >\n      {children}\n    </HeaderText>\n  </Box>\n);\n\nChannelHeaderLabel.propTypes = {\n  children: PropTypes.node.isRequired,\n  centerText: PropTypes.bool,\n};\n\nChannelHeaderLabel.defaultProps = {\n  centerText: false,\n};\n"
  },
  {
    "path": "src/components/ChannelHeader/index.js",
    "content": "export * from './ChannelHeader.component';\n"
  },
  {
    "path": "src/components/ChannelList/ChannelList.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Sortable } from '@shopify/draggable';\nimport { Box } from '../design-system';\nimport { Channel } from '../Channel';\nimport { AddChannelButton } from '../AddChannelButton';\n\nconst ChannelListBox = Box.extend`\n  outline: none;\n`;\n\nexport class ChannelListComponent extends React.Component {\n  componentDidMount() {\n    // eslint-disable-next-line\n    const sortable = new Sortable([this.channelContainer], {\n      draggable: '.wds-draggable',\n      handle: '.wds-channel-handle',\n      mirror: {\n        constrainDimensions: true,\n      },\n    });\n\n    sortable.on('sortable:stop', ({ oldIndex, newIndex }) => {\n      const { onUpdateChannelOrder } = this.props;\n      onUpdateChannelOrder(oldIndex, newIndex);\n    });\n  }\n\n  render() {\n    const { channels } = this.props;\n    return (\n      <ChannelListBox mt={2} innerRef={(el) => { this.channelContainer = el; }}>\n        {channels.map(channel => <Channel key={channel.id} channel={channel} />)}\n        <AddChannelButton />\n      </ChannelListBox>\n    );\n  }\n}\n\nChannelListComponent.propTypes = {\n  channels: PropTypes.arrayOf(PropTypes.object).isRequired,\n  onUpdateChannelOrder: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/ChannelList/ChannelList.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { ChannelListComponent } from './ChannelList.component';\nimport { channelListSelectors } from './ChannelList.selectors';\nimport { toggleNote, updateChannelOrder } from '../../common';\n\nconst mapDispatchToProps = {\n  toggleNote,\n  updateChannelOrder,\n};\n\nconst handlers = withHandlers({\n  onUpdateChannelOrder: (props) => (oldIndex, newIndex) => {\n    const { updateChannelOrder: updateChannelOrderConnected } = props;\n    updateChannelOrderConnected(oldIndex, newIndex);\n  },\n});\n\nexport const ChannelList = compose(\n  connect(channelListSelectors, mapDispatchToProps),\n  handlers,\n)(ChannelListComponent);\n"
  },
  {
    "path": "src/components/ChannelList/ChannelList.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { channelsSelector } from '../../common';\n\nexport const channelListSelectors = createStructuredSelector({\n  channels: channelsSelector,\n});\n"
  },
  {
    "path": "src/components/ChannelList/index.js",
    "content": "export * from './ChannelList.container';\n"
  },
  {
    "path": "src/components/FancyButton.component.jsx",
    "content": "import { variant } from 'styled-system';\nimport { Button } from './design-system';\n\nconst fancyButtonStyle = variant({\n  key: 'fancyButtons',\n});\n\nexport const FancyButton = Button.extend`\n  ${fancyButtonStyle}\n  transition: box-shadow 0.2s, transform 0.2s;\n  text-transform: uppercase;\n  height: calc(100% - 4px);\n  \n  &:active: {\n    transform: translateY(0.3em);\n  }\n`;\n"
  },
  {
    "path": "src/components/FlashMessage/FlashMessage.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport * as animol from 'animol';\nimport { FLASH_MESSAGES } from '../../common';\nimport {\n  Box,\n  HoverButton,\n} from '../design-system';\nimport { SampleLoadError } from '../SampleLoadError.component';\nimport { PresetSaved } from '../PresetSaved.component';\nimport { PresetDeleted } from '../PresetDeleted.component';\nimport { timedCallback } from '../timedCallback.hoc';\n\nconst getMessageComponent = (messageKey) => {\n  switch (messageKey) {\n    case FLASH_MESSAGES.SAMPLE_LOAD_ERROR:\n      return SampleLoadError;\n    case FLASH_MESSAGES.PRESET_SAVED:\n      return PresetSaved;\n    case FLASH_MESSAGES.PRESET_DELETED:\n      return PresetDeleted;\n    default:\n      return undefined;\n  }\n};\n\nexport class FlashMessageComponent extends React.Component {\n  componentDidMount() {\n    this.animateBox();\n  }\n\n  componentDidUpdate() {\n    this.animateBox();\n  }\n\n  animateBox() {\n    const { flashMessageVisible, messageKey } = this.props;\n    if (messageKey && flashMessageVisible) {\n      this.flashBox.style.display = 'block';\n      animol.css(\n        this.flashBox,\n        500,\n        { opacity: 0, transform: { translateY: '10%' } },\n        { opacity: 1, transform: { translateY: '0%' } },\n        animol.Easing.easeOutCubic,\n      );\n    } else if (messageKey) {\n      const animation = animol.css(\n        this.flashBox,\n        200,\n        { opacity: 1 },\n        { opacity: 0 },\n        animol.Easing.easeInCubic,\n      );\n      animation.promise.then(() => {\n        this.flashBox.style.display = 'none';\n      });\n    }\n  }\n\n  render() {\n    const { messageKey, onDismiss } = this.props;\n    const Message = getMessageComponent(messageKey);\n    const DisappearingMessage = timedCallback(onDismiss, 6000)(Message);\n    return Message\n      ? (\n        <Box\n          bg=\"white\"\n          position=\"fixed\"\n          bottom={0}\n          right={0}\n          m={3}\n          boxShadow=\"0 0.5rem 3rem rgba(0,0,0,0.9)\"\n          maxWidth=\"30rem\"\n          innerRef={(comp) => { this.flashBox = comp; }}\n          opacity=\"0\"\n        >\n          <Box\n            innerRef={(comp) => { this.flashMessage = comp; }}\n            p={4}\n          >\n            <DisappearingMessage onDismiss={onDismiss} />\n            <HoverButton\n              bg=\"transparent\"\n              m={1}\n              display=\"flex\"\n              justifyContent=\"space-between\"\n              onClick={onDismiss}\n              fontSize={3}\n              alignItems=\"center\"\n              alignSelf=\"center\"\n              transitionSpeed=\"0.2s\"\n              position=\"absolute\"\n              right=\"0.3rem\"\n              top=\"0.3rem\"\n              width=\"0.8rem\"\n              height=\"0.8rem\"\n              hoverOpacity=\"0.5\"\n              p={0}\n            >\n              <svg width=\"100%\" height=\"100%\" viewBox=\"169 215 170 170\" xmlns=\"http://www.w3.org/2000/svg\">\n                <path d=\"M 169.72656,371.38672 L 238.67188,302.44141 L 169.92188,233.69141 L 181.25,222.36328 L 250,291.11328 L 318.75,222.36328 L 329.6875,233.49609 L 260.9375,302.24609 L 330.07812,371.38672 L 318.94531,382.71484 L 249.80469,313.57422 L 180.85938,382.51953 L 169.72656,371.38672 z\" />\n              </svg>\n            </HoverButton>\n          </Box>\n        </Box>\n      )\n      : null;\n  }\n}\n\nFlashMessageComponent.propTypes = {\n  messageKey: PropTypes.string,\n  onDismiss: PropTypes.func.isRequired,\n  flashMessageVisible: PropTypes.bool,\n};\n\nFlashMessageComponent.defaultProps = {\n  messageKey: null,\n  flashMessageVisible: false,\n};\n"
  },
  {
    "path": "src/components/FlashMessage/FlashMessage.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { FlashMessageComponent } from './FlashMessage.component';\nimport { clearFlashMessage } from '../../common';\nimport { flashMessageSelectors } from './FlashMessage.selectors';\n\nconst mapDispatchToProps = {\n  clearFlashMessage,\n};\n\nconst handlers = withHandlers({\n  onDismiss: (props) => () => {\n    const { clearFlashMessage: connectedClearFlashMessage } = props;\n    connectedClearFlashMessage();\n  },\n});\n\nexport const FlashMessage = compose(\n  connect(flashMessageSelectors, mapDispatchToProps),\n  handlers,\n)(FlashMessageComponent);\n"
  },
  {
    "path": "src/components/FlashMessage/FlashMessage.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { flashMessageKeySelector, flashMessageVisibleSelector } from '../../common';\n\nexport const flashMessageSelectors = createStructuredSelector({\n  messageKey: flashMessageKeySelector,\n  flashMessageVisible: flashMessageVisibleSelector,\n});\n"
  },
  {
    "path": "src/components/FlashMessage/index.js",
    "content": "export * from './FlashMessage.container';\n"
  },
  {
    "path": "src/components/GithubLink.component.jsx",
    "content": "import React from 'react';\nimport {\n  Image,\n  HoverLink,\n  Text,\n  Box,\n} from './design-system';\nimport octocat from '../assets/images/github.svg';\n\nexport const GithubLink = () => (\n  <HoverLink\n    href=\"https://www.github.com/stufreen/web-drum-sequencer\"\n    description=\"Source code on Github\"\n    opacity={0.3}\n    hoverOpacity={0.8}\n    transitionSpeed=\"0.1s\"\n  >\n    <Box display=\"flex\" alignItems=\"center\">\n      <Text mr={2} color=\"white\" fontWeight=\"600\">\n        Github\n      </Text>\n      <Image src={octocat} width=\"2rem\" height=\"2rem\" alt=\"Github Logo\" />\n    </Box>\n  </HoverLink>\n);\n"
  },
  {
    "path": "src/components/InfoKnob.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Knob } from './Knob.component';\nimport { ControlLabel, Box } from './design-system';\n\nexport const InfoKnob = ({\n  label,\n  minLabel,\n  maxLabel,\n  ...rest\n}) => (\n  <Box>\n    <ControlLabel fontWeight=\"bold\" mb={1} textAlign=\"center\">\n      {label}\n    </ControlLabel>\n    <Box display=\"flex\" alignItems=\"baseline\">\n      <ControlLabel opacity=\"0.5\" fontSize={1}>\n        {minLabel}\n      </ControlLabel>\n      <Knob size={45} {...rest} />\n      <ControlLabel opacity=\"0.5\" fontSize={1}>\n        {maxLabel}\n      </ControlLabel>\n    </Box>\n  </Box>\n);\n\nInfoKnob.propTypes = {\n  label: PropTypes.string.isRequired,\n  minLabel: PropTypes.string.isRequired,\n  maxLabel: PropTypes.string.isRequired,\n};\n"
  },
  {
    "path": "src/components/InstallButton.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-redux';\nimport { HoverButton } from './design-system';\nimport { promptToInstall } from '../services/pwaInstall';\nimport { canInstallSelector } from '../common/window/window.selectors';\n\nconst isStandalone = window.matchMedia('(display-mode: standalone)').matches;\n\nconst InstallButtonComponent = ({ canInstall }) => (canInstall && !isStandalone\n  ? (\n    <HoverButton\n      onClick={() => {\n        promptToInstall();\n      }}\n      width=\"auto\"\n      bg=\"blue\"\n      color=\"white\"\n      hoverColor=\"nearWhite\"\n      hoverBg=\"darkBlue\"\n      transitionSpeed=\"0.2s\"\n      p=\"0.6rem 1.2rem\"\n    >\n      INSTALL\n    </HoverButton>\n  )\n  : null);\n\nInstallButtonComponent.propTypes = {\n  canInstall: PropTypes.bool.isRequired,\n};\n\nconst mapStateToProps = state => ({\n  canInstall: canInstallSelector(state),\n});\n\nexport const InstallButton = connect(mapStateToProps)(InstallButtonComponent);\n"
  },
  {
    "path": "src/components/Knob.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\n// import '../assets/js/webcomponents-lite';\nimport knobImage from '../assets/images/maschine-50.png';\nimport '../assets/js/webaudio-controls';\n\nexport class Knob extends React.Component {\n  componentDidMount() {\n    const { onChange } = this.props;\n    this.knob.addEventListener('input', onChange);\n  }\n\n  componentDidUpdate() {\n    const { value } = this.props;\n    this.knob.setValue(value);\n  }\n\n  render() {\n    const { size, value, ...rest } = this.props;\n    return (\n      <webaudio-knob\n        ref={(element) => {\n          this.knob = element;\n        }}\n        src={knobImage}\n        sprites=\"50\"\n        min=\"0\"\n        max=\"100\"\n        width={size}\n        height={size}\n        value={value}\n        {...rest}\n      />\n    );\n  }\n}\n\nKnob.propTypes = {\n  onChange: PropTypes.func.isRequired,\n  size: PropTypes.number.isRequired,\n  value: PropTypes.number.isRequired,\n};\n"
  },
  {
    "path": "src/components/LabelBox.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport theme from '../styles/theme';\nimport { Box, Text } from './design-system';\n\nconst HoverBox = Box.extend`\n  transition: border-color 0.2s;\n\n  &:hover {\n    ${({ hoverEffect }) => (\n    hoverEffect\n      ? `border-color: ${theme.colors.gray};`\n      : '')}\n  }\n`;\n\nexport const LabelBox = ({ label, children, hoverEffect }) => (\n  <HoverBox\n    display=\"flex\"\n    border=\"2px solid\"\n    borderColor=\"steel\"\n    borderRadius=\"0.5rem\"\n    p={2}\n    position=\"relative\"\n    alignItems=\"center\"\n    hoverEffect={hoverEffect}\n  >\n    <Text\n      position=\"absolute\"\n      left=\"0.5rem\"\n      top=\"-0.6em\"\n      color=\"gray\"\n      fontSize=\"0.6rem\"\n      fontWeight=\"600\"\n      bg=\"nearBlack\"\n      pl={1}\n      pr={1}\n      letterSpacing=\"0.1em\"\n      borderRadius=\"3px\"\n    >\n      {label}\n    </Text>\n    {children}\n  </HoverBox>\n);\n\nLabelBox.propTypes = {\n  label: PropTypes.string.isRequired,\n  children: PropTypes.node.isRequired,\n  hoverEffect: PropTypes.bool,\n};\n\nLabelBox.defaultProps = {\n  hoverEffect: false,\n};\n"
  },
  {
    "path": "src/components/Logo.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport theme from '../styles/theme';\n\nexport const Logo = ({ color, width }) => (\n  <svg width={width} viewBox=\"0 0 1032 260\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xlink=\"http://www.w3.org/1999/xlink\">\n    <g stroke=\"none\" strokeWidth=\"1\" fill=\"none\" fillRule=\"evenodd\">\n      <g>\n        <path d=\"M166.153548,105.215548 C168.747042,110.401379 186.801754,147.300561 206.153214,187.19157 C225.604423,227.082578 241.66414,259.593749 241.863639,259.394294 C242.063139,259.194839 260.71635,201.851515 283.459302,132.042251 L324.655966,4.98938975 L301.115016,4.69020719 C288.247292,4.59047967 277.274815,4.78993471 276.875816,5.18884479 C276.377067,5.68748239 266.601588,36.7027412 255.030612,74.3000165 C243.559386,111.897292 233.684156,143.510916 233.185408,144.607918 C232.287659,146.801924 235.778902,153.982305 190.193247,58.2438857 C175.131029,26.6302617 162.562555,0.900561409 162.263306,1.00028893 C161.964056,1.19974397 146.303339,33.7109156 127.450629,73.3027412 C108.498168,112.794839 92.7377011,144.807373 92.3387019,144.408463 C91.9397027,144.009553 81.8649738,112.296202 69.8949988,74.0008339 L48.1495443,4.49075215 L23.8105952,4.49075215 C1.16739249,4.49075215 -0.428604173,4.59047967 0.0701447848,6.28584751 C2.66363936,14.4635042 77.7752324,254.108736 78.6729805,256.801379 C79.7702282,260.291842 80.0694776,259.793204 120.667643,178.016638 L161.465307,95.7414333 L166.153548,105.215548 Z\" fill={color} />\n        <path d=\"M491.178482,252.321526 C510.529942,249.429428 521.502419,245.938965 536.764137,238.259946 C579.856047,216.120436 605.591493,171.143324 602.997998,122.476294 C600.105254,66.7286104 563.197832,22.6490463 507.637198,8.48773842 C491.776981,4.4986376 482.101251,4 420.854879,4 L363,4 L363,128.459946 C363,196.972752 363.299249,253.318801 363.698249,253.617984 C363.997498,254.016894 389.932444,254.316076 421.253878,254.316076 C472.92427,254.316076 479.408007,254.116621 491.178482,252.321526 Z\" fill={color} />\n        <path d=\"M410.797998,209.957493 C410.299249,209.558583 410,172.958583 410,128.579837 L410,48 L440.722936,48 C472.842369,48 486.208841,48.8975477 495.685071,51.5901907 C524.313261,59.8675749 542.268224,75.4250681 551.345455,99.9580381 C555.734445,111.626158 557.031193,118.706812 556.931443,130.275204 C556.831693,145.932425 552.342952,160.19346 543.066222,174.354768 C532.193495,190.710082 515.13628,202.178747 493.291076,207.763488 C486.208841,209.558583 481.321101,209.857766 448.40367,210.256676 C428.154462,210.555858 411.196997,210.456131 410.797998,209.957493 Z\" fill={theme.colors.nearBlack} fillRule=\"nonzero\" />\n        <path d=\"M651.448707,252.822888 C697.632861,243.847411 726.261051,216.123161 737.333278,169.550409 C738.530275,164.564033 739.228524,154.790736 739.727273,135.144414 C740.525271,106.622343 741.722269,97.8463215 745.91176,86.3776567 C753.093745,67.1302452 772.445204,52.4702997 794.290409,49.8773842 C804.265388,48.5809264 803.666889,50.2762943 803.36764,26.1422343 L803.06839,5 L794.589658,5.09972752 C770.649708,5.39891008 741.522769,18.3634877 723.867056,36.7133515 C710.301084,50.6752044 701.922102,67.0305177 696.635363,89.2697548 C695.039366,96.0512262 694.540617,103.431063 693.842369,125.6703 C693.04437,155.488828 692.046872,162.968392 687.258882,174.237602 C679.079399,193.684469 662.321435,206.050681 639.478732,209.341689 L631.498749,210.53842 L631.1995,232.677929 L631,254.817439 L635.88774,254.817439 C638.680734,254.817439 645.663219,253.919891 651.448707,252.822888 Z\" fill={color} />\n        <path d=\"M1031.60033,126.663868 C1031.79983,5.39520324 1031.70008,-0.289265422 1030.00434,0.00991713969 C1029.10659,0.209372181 1011.15163,10.5810343 990.10442,23.1467019 L952,45.9843041 L952,69.8191814 C952,89.1663204 952.299249,93.6540588 953.296747,93.2551487 C953.994996,92.9559662 961.276731,88.2687727 969.356464,82.6840316 C977.436197,77.0992904 984.318932,72.611552 984.518432,72.611552 C984.717932,72.611552 984.917431,113.499835 984.917431,163.363596 L984.917431,254.115639 L1008.15913,253.916184 L1031.30108,253.617002 L1031.60033,126.663868 Z\" fill={color} />\n        <polygon fill={color} points=\"905.799833 126.446866 905.799833 107 865.899917 107 826 107 826 126.446866 826 145.893733 865.899917 145.893733 905.799833 145.893733\" />\n      </g>\n    </g>\n  </svg>\n);\n\nLogo.propTypes = {\n  width: PropTypes.string.isRequired,\n  color: PropTypes.string.isRequired,\n};\n"
  },
  {
    "path": "src/components/Marker/Marker.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Box } from '../design-system';\nimport { getCurrentBeat } from '../../services/audioContext';\nimport theme from '../../styles/theme';\n\nconst Container = Box.extend`\n  overflow: hidden;\n`;\n\nexport class MarkerComponent extends React.PureComponent {\n  componentDidMount() {\n    this.updateMarker.bind(this);\n    this.updateMarker();\n  }\n\n  updateMarker() {\n    const { playing, startTime, bpm } = this.props;\n    if (playing) {\n      const currentBeat = getCurrentBeat(bpm, startTime);\n      const progress = (currentBeat - 1) / 4 * 100;\n      this.marker.style.width = `${progress}%`;\n    }\n    window.requestAnimationFrame(() => {\n      this.updateMarker();\n    });\n  }\n\n  render() {\n    const { children } = this.props;\n    return (\n      <Container flex=\"1 1 auto\" position=\"relative\">\n        <div\n          ref={(ref) => { this.marker = ref; }}\n          style={{\n            height: '100%',\n            backgroundColor: theme.colors.darkGray,\n            position: 'absolute',\n            width: 0,\n          }}\n        />\n        <Box position=\"absolute\" display=\"flex\" width=\"100%\">\n          {children}\n        </Box>\n      </Container>\n    );\n  }\n}\n\nMarkerComponent.defaultProps = {\n  startTime: null,\n};\n\nMarkerComponent.propTypes = {\n  startTime: PropTypes.number,\n  bpm: PropTypes.number.isRequired,\n  playing: PropTypes.bool.isRequired,\n  children: PropTypes.node.isRequired,\n};\n"
  },
  {
    "path": "src/components/Marker/Marker.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose } from 'recompose';\nimport { MarkerComponent } from './Marker.component';\nimport { markerSelectors } from './Marker.selectors';\n\nexport const Marker = compose(\n  connect(markerSelectors, null),\n)(MarkerComponent);\n"
  },
  {
    "path": "src/components/Marker/Marker.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport {\n  bpmSelector,\n  startTimeSelector,\n  playingSelector,\n} from '../../common';\n\nexport const markerSelectors = createStructuredSelector({\n  bpm: bpmSelector,\n  startTime: startTimeSelector,\n  playing: playingSelector,\n});\n"
  },
  {
    "path": "src/components/Marker/index.js",
    "content": "export * from './Marker.container';\n"
  },
  {
    "path": "src/components/MasterControls/MasterControls.component.jsx",
    "content": "import React from 'react';\nimport { Box } from '../design-system';\nimport { PlayButton } from '../PlayButton';\nimport { BPMInput } from '../BPMInput';\nimport { PresetSelector } from '../PresetSelector';\nimport { PatternSelector } from '../PatternSelector';\nimport { SwingControl } from '../SwingControl';\nimport { VolumeMeter } from '../VolumeMeter.component';\n\nexport const MasterControls = () => (\n  <Box\n    mb={3}\n    display=\"flex\"\n    alignItems=\"stretch\"\n    height=\"3rem\"\n    justifyContent=\"space-between\"\n  >\n    <Box display=\"flex\" mr={2}>\n      <Box mr={2}>\n        <PlayButton />\n      </Box>\n      <BPMInput />\n      <SwingControl />\n      <VolumeMeter />\n    </Box>\n    <Box display=\"flex\">\n      <PatternSelector />\n      <Box ml={2} width=\"15rem\">\n        <PresetSelector />\n      </Box>\n    </Box>\n  </Box>\n);\n"
  },
  {
    "path": "src/components/MasterControls/index.js",
    "content": "export * from './MasterControls.component';\n"
  },
  {
    "path": "src/components/Modal.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Box } from './design-system';\n\nexport const Modal = ({ children, show }) => (\n  <Box\n    position=\"fixed\"\n    left={0}\n    top={0}\n    width=\"100%\"\n    height=\"100%\"\n    bg=\"black80\"\n    display={show ? 'flex' : 'none'}\n    zIndex={99}\n    justifyContent=\"center\"\n    alignItems=\"center\"\n  >\n    {children}\n  </Box>\n);\n\nModal.propTypes = {\n  children: PropTypes.node.isRequired,\n  show: PropTypes.bool.isRequired,\n};\n"
  },
  {
    "path": "src/components/MuteSolo/MuteSolo.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Box, Button } from '../design-system';\n\nconst MSButton = Button.extend`\n  width: 0.8rem;\n  height: 0.8rem;\n  padding: 0;\n  border-radius: 100%;\n  transition: all 0.1s;\n`;\n\nexport const MuteSoloComponent = ({ onPressMuted, onPressSolo, channel }) => (\n  <Box\n    justifyContent=\"space-around\"\n    width=\"1.2rem\"\n    display=\"flex\"\n    ml={2}\n    height=\"100%\"\n    flexDirection=\"column\"\n  >\n    <MSButton\n      bg={channel.solo ? 'yellow' : 'yellow30'}\n      onClick={onPressSolo}\n    />\n    <MSButton\n      bg={channel.muted ? 'brightRed' : 'brightRed30'}\n      onClick={onPressMuted}\n    />\n  </Box>\n);\n\nMuteSoloComponent.propTypes = {\n  onPressMuted: PropTypes.func.isRequired,\n  onPressSolo: PropTypes.func.isRequired,\n  channel: PropTypes.shape({\n    solo: PropTypes.bool,\n    muted: PropTypes.bool,\n  }).isRequired,\n};\n"
  },
  {
    "path": "src/components/MuteSolo/MuteSolo.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { MuteSoloComponent } from './MuteSolo.component';\nimport {\n  setChannelMuted,\n  setChannelSolo,\n} from '../../common';\n\nconst mapDispatchToProps = {\n  setChannelMuted,\n  setChannelSolo,\n};\n\nconst handlers = withHandlers({\n  onPressMuted: (props) => () => {\n    const { channel, setChannelMuted: setChannelMutedConnected } = props;\n    setChannelMutedConnected(channel.id, !channel.muted);\n  },\n  onPressSolo: (props) => () => {\n    const { channel, setChannelSolo: setChannelSoloConnected } = props;\n    setChannelSoloConnected(channel.id, !channel.solo);\n  },\n});\n\nexport const MuteSolo = compose(\n  connect(null, mapDispatchToProps),\n  handlers,\n)(MuteSoloComponent);\n"
  },
  {
    "path": "src/components/MuteSolo/index.js",
    "content": "export * from './MuteSolo.container';\n"
  },
  {
    "path": "src/components/PatternSelector/PatternSelector.component.jsx",
    "content": "import React from 'react';\nimport * as R from 'ramda';\nimport PropTypes from 'prop-types';\nimport { LabelBox } from '../LabelBox';\nimport { HoverButton } from '../design-system';\n\nexport const PatternSelectorComponent = ({ onSelectPattern, pattern }) => {\n  const buttons = R.range(0, 8).map(buttonNumber => (\n    <HoverButton\n      key={`preset-${buttonNumber}`}\n      p={0}\n      height=\"1.3rem\"\n      width=\"1.3rem\"\n      borderRadius={2}\n      bg={pattern === buttonNumber ? 'primary' : 'lightGray'}\n      ml=\"1px\"\n      mr=\"1px\"\n      mt=\"1px\"\n      transitionSpeed=\"0.1s\"\n      onClick={() => {\n        onSelectPattern(buttonNumber);\n      }}\n      fontWeight=\"500\"\n      fontSize=\"0.7em\"\n      color=\"rgba(0,0,0,0.5)\"\n      activeBg=\"primaryDark\"\n      disabled={pattern === buttonNumber}\n      aria-label={`Enable pattern ${buttonNumber}`}\n      lineHeight=\"1.4em\"\n    >\n      {buttonNumber + 1}\n    </HoverButton>\n  ));\n\n  return (\n    <LabelBox label=\"PATTERNS\" hoverEffect>\n      {buttons}\n    </LabelBox>\n  );\n};\n\nPatternSelectorComponent.propTypes = {\n  pattern: PropTypes.number.isRequired,\n  onSelectPattern: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/PatternSelector/PatternSelector.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { PatternSelectorComponent } from './PatternSelector.component';\nimport { patternSelectorSelectors } from './PatternSelector.selectors';\nimport { setPattern } from '../../common';\n\nconst mapDispatchToProps = {\n  setPattern,\n};\n\nexport const PatternSelector = compose(\n  connect(patternSelectorSelectors, mapDispatchToProps),\n  withHandlers({\n    onSelectPattern: (props) => (patternIndex) => {\n      const {\n        setPattern: connectedSetPattern,\n      } = props;\n      connectedSetPattern(patternIndex);\n    },\n  }),\n)(PatternSelectorComponent);\n"
  },
  {
    "path": "src/components/PatternSelector/PatternSelector.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { patternSelector } from '../../common';\n\nexport const patternSelectorSelectors = createStructuredSelector({\n  pattern: patternSelector,\n});\n"
  },
  {
    "path": "src/components/PatternSelector/index.js",
    "content": "export * from './PatternSelector.container';\n"
  },
  {
    "path": "src/components/PlayButton/PlayButton.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { FancyButton } from '../FancyButton.component';\nimport { Text } from '../design-system';\n\nconst StyledPlayButton = FancyButton.extend`\n  margin-bottom: 1px;\n  width: 8rem;\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  justify-content: center;\n`;\n\nexport function PlayButtonComponent({\n  startPlaybackAndResume,\n  stopPlayback,\n  playing,\n}) {\n  return playing ? (\n    <StyledPlayButton onClick={stopPlayback} variant=\"red\">\n      <Text mr={2} fontSize={1}>\n        Stop\n      </Text>\n      <svg\n        width=\"0.9em\"\n        height=\"0.9em\"\n        viewBox=\"0 0 371 371\"\n        version=\"1.1\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <g stroke=\"none\" fill=\"none\" fillRule=\"evenodd\">\n          <rect fill=\"#FFFFFF\" x=\"0\" y=\"0\" width=\"371\" height=\"371\" />\n        </g>\n      </svg>\n    </StyledPlayButton>\n  ) : (\n    <StyledPlayButton onClick={startPlaybackAndResume} variant=\"green\">\n      <Text mr={2} fontSize={1}>\n        PLAY\n      </Text>\n      <svg\n        width=\"1.1em\"\n        height=\"0.9em\"\n        viewBox=\"0 0 452 396\"\n        version=\"1.1\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <g stroke=\"none\" strokeWidth=\"1\" fill=\"none\" fillRule=\"evenodd\">\n          <g fill=\"#FFFFFF\">\n            <rect stroke=\"#FFFFFF\" x=\"0.5\" y=\"11.5\" width=\"131\" height=\"369\" />\n            <polygon points=\"202.140625 0 202.140625 396 452 198\" />\n          </g>\n        </g>\n      </svg>\n    </StyledPlayButton>\n  );\n}\n\nPlayButtonComponent.propTypes = {\n  startPlaybackAndResume: PropTypes.func.isRequired,\n  stopPlayback: PropTypes.func.isRequired,\n  playing: PropTypes.bool.isRequired,\n};\n"
  },
  {
    "path": "src/components/PlayButton/PlayButton.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose } from 'recompose';\nimport { PlayButtonComponent } from './PlayButton.component';\nimport { playButtonSelectors } from './PlayButton.selectors';\nimport { startPlaybackAndResume, stopPlayback } from '../../common';\n\nconst mapDispatchToProps = {\n  startPlaybackAndResume,\n  stopPlayback,\n};\n\nexport const PlayButton = compose(\n  connect(playButtonSelectors, mapDispatchToProps),\n)(PlayButtonComponent);\n"
  },
  {
    "path": "src/components/PlayButton/PlayButton.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { playingSelector } from '../../common';\n\nexport const playButtonSelectors = createStructuredSelector({\n  playing: playingSelector,\n});\n"
  },
  {
    "path": "src/components/PlayButton/index.js",
    "content": "export * from './PlayButton.container';\n"
  },
  {
    "path": "src/components/PresetDeleted.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Box,\n  Text,\n  HoverButton,\n} from './design-system';\n\nexport const PresetDeleted = ({ onDismiss }) => (\n  <Box>\n    <Text\n      fontWeight=\"400\"\n      color=\"nearBlack\"\n      fontSize={2}\n      mb={3}\n    >\n      User preset deleted.\n    </Text>\n    <Box display=\"flex\" justifyContent=\"flex-end\">\n      <HoverButton\n        bg=\"blue\"\n        hoverBg=\"darkBlue\"\n        onClick={onDismiss}\n        width=\"7rem\"\n        color=\"white\"\n        display=\"block\"\n        py={2}\n      >\n        OK\n      </HoverButton>\n    </Box>\n  </Box>\n);\n\nPresetDeleted.propTypes = {\n  onDismiss: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/PresetSaved.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Box,\n  Text,\n  HoverButton,\n} from './design-system';\n\nexport const PresetSaved = ({ onDismiss }) => (\n  <Box>\n    <Text\n      fontWeight=\"400\"\n      color=\"nearBlack\"\n      fontSize={2}\n      mb={3}\n    >\n      User preset saved.\n    </Text>\n    <Box display=\"flex\" justifyContent=\"flex-end\">\n      <HoverButton\n        bg=\"blue\"\n        hoverBg=\"darkBlue\"\n        onClick={onDismiss}\n        width=\"7rem\"\n        color=\"white\"\n        display=\"block\"\n        py={2}\n      >\n        OK\n      </HoverButton>\n    </Box>\n  </Box>\n);\n\nPresetSaved.propTypes = {\n  onDismiss: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/PresetSelector/PresetSelector.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport Select from 'react-select';\nimport * as R from 'ramda';\nimport theme from '../../styles/theme';\nimport { Box, Text } from '../design-system';\nimport { SavePresetModal } from '../SavePresetModal';\n\nexport const PresetSelectorComponent = ({\n  onSelectPreset,\n  presets,\n  currentPreset,\n  isEdited,\n  userPresets,\n}) => {\n  const defaultPresetOptions = presets.map(preset => ({\n    label: preset.name,\n    value: preset,\n  }));\n\n  const userPresetOptions = userPresets.map(preset => ({\n    label: preset.name,\n    value: preset,\n  }));\n\n  const groupedOptions = [\n    {\n      label: 'Default',\n      options: defaultPresetOptions,\n    },\n    {\n      label: 'User',\n      options: userPresetOptions,\n    },\n    {\n      label: 'Memory',\n      options: [\n        {\n          label: 'Save As...',\n          value: 'SAVE_PRESET_AS',\n        },\n        {\n          label: `Save “${currentPreset.name}”`,\n          value: 'SAVE_PRESET',\n          disabled: !isEdited || (defaultPresetOptions.find(\n            option => option.label === currentPreset.name,\n          ) !== undefined),\n        },\n        {\n          label: `Delete “${currentPreset.name}”`,\n          value: 'DELETE_PRESET',\n          disabled: (defaultPresetOptions.find(\n            option => option.label === currentPreset.name,\n          ) !== undefined),\n        },\n      ],\n    },\n  ];\n\n  let selectedOption = [...defaultPresetOptions, ...userPresetOptions].find(\n    option => option.label === currentPreset.name,\n  );\n  if (isEdited && selectedOption) {\n    selectedOption = R.clone(selectedOption);\n    selectedOption.label += ' *';\n  }\n\n  return (\n    <Box height=\"100%\" position=\"relative\">\n      <Text\n        position=\"absolute\"\n        left=\"0.5rem\"\n        top=\"-0.6em\"\n        color=\"gray\"\n        fontSize=\"0.6rem\"\n        fontWeight=\"600\"\n        bg=\"nearBlack\"\n        pl={1}\n        pr={1}\n        letterSpacing=\"0.1em\"\n        zIndex={1}\n        borderRadius=\"3px\"\n      >\n        PRESETS\n      </Text>\n      <Select\n        isEdited={isEdited}\n        options={groupedOptions}\n        onChange={onSelectPreset}\n        value={selectedOption}\n        aria-label=\"Select Preset\"\n        isOptionDisabled={({ value, disabled }) => {\n          if (value === 'SAVE_PRESET') {\n            return disabled;\n          }\n          if (value === 'DELETE_PRESET') {\n            return disabled;\n          }\n          return false;\n        }}\n        isSearchable={false}\n        styles={{\n          container: styles => ({\n            ...styles,\n            height: '100%',\n          }),\n          control: styles => ({\n            ...styles,\n            backgroundColor: 'black',\n            border: `2px solid ${theme.colors.steel}`,\n            height: '100%',\n            borderRadius: '0.5em',\n          }),\n          singleValue: styles => ({\n            ...styles,\n            color: theme.colors.nearWhite,\n          }),\n          option: styles => ({\n            ...styles,\n            padding: '0.4em 1em',\n          }),\n        }}\n      />\n      <SavePresetModal />\n    </Box>\n  );\n};\n\nPresetSelectorComponent.propTypes = {\n  isEdited: PropTypes.bool.isRequired,\n  currentPreset: PropTypes.shape({\n    name: PropTypes.string.isRequired,\n  }).isRequired,\n  onSelectPreset: PropTypes.func.isRequired,\n  presets: PropTypes.arrayOf(PropTypes.object).isRequired,\n  userPresets: PropTypes.arrayOf(PropTypes.object).isRequired,\n};\n"
  },
  {
    "path": "src/components/PresetSelector/PresetSelector.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withProps, withHandlers } from 'recompose';\nimport { PresetSelectorComponent } from './PresetSelector.component';\nimport { presetSelectorSelectors } from './PresetSelector.selectors';\nimport {\n  setBPM,\n  setPreset,\n  setPattern,\n  savePresetAs,\n  deletePreset,\n  setPresetPrompt,\n  doSavePreset,\n  loadPreset,\n  erasePreset,\n} from '../../common';\nimport presets from '../../presets';\n\nconst mapDispatchToProps = {\n  setBPM,\n  setPreset,\n  setPattern,\n  savePresetAs,\n  deletePreset,\n  setPresetPrompt,\n  doSavePreset,\n  loadPreset,\n  erasePreset,\n};\n\nexport const PresetSelector = compose(\n  connect(presetSelectorSelectors, mapDispatchToProps),\n  withProps({\n    presets,\n  }),\n  withHandlers({\n    onSelectPreset: (props) => ({ value }) => {\n      const {\n        doSavePreset: connectedDoSavePreset,\n        setPresetPrompt: connectedSetPresetPrompt,\n        erasePreset: connectedErasePreset,\n        loadPreset: connectedLoadPreset,\n        currentPreset,\n      } = props;\n      switch (value) {\n        case 'SAVE_PRESET':\n          connectedDoSavePreset(currentPreset.name);\n          break;\n        case 'DELETE_PRESET':\n          connectedErasePreset(currentPreset.name);\n          break;\n        case 'SAVE_PRESET_AS':\n          connectedSetPresetPrompt(true);\n          break;\n        default:\n          connectedLoadPreset(value);\n          break;\n      }\n    },\n  }),\n)(PresetSelectorComponent);\n"
  },
  {
    "path": "src/components/PresetSelector/PresetSelector.selectors.js",
    "content": "import { createStructuredSelector, createSelector } from 'reselect';\nimport * as R from 'ramda';\nimport {\n  presetSelector,\n  notesSelector,\n  userPresetsSelector,\n  presetPromptOpenSelector,\n  currentStateSelector,\n} from '../../common';\nimport presets from '../../presets';\n\n// presetSelector returns the preset name - this will get the whole preset object\nconst currentPresetSelector = createSelector(\n  presetSelector,\n  userPresetsSelector,\n  (presetName, userPresets) => [...presets, ...userPresets].find(\n    (preset) => preset.name === presetName,\n  ),\n);\n\n// Indicates if the preset is a \"stock\" preset or has been modified by user (not saved)\nconst isEditedSelector = createSelector(\n  currentPresetSelector,\n  currentStateSelector,\n  (preset, currentState) => !R.equals(R.omit(['name'], preset), currentState),\n);\n\nexport const presetSelectorSelectors = createStructuredSelector({\n  isEdited: isEditedSelector,\n  currentPreset: currentPresetSelector,\n  userPresets: userPresetsSelector,\n  notes: notesSelector,\n  currentState: currentStateSelector,\n  presetPromptOpen: presetPromptOpenSelector,\n});\n"
  },
  {
    "path": "src/components/PresetSelector/index.js",
    "content": "export * from './PresetSelector.container';\n"
  },
  {
    "path": "src/components/SampleLoadError.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Box,\n  Text,\n  HoverButton,\n} from './design-system';\n\nexport const SampleLoadError = ({ onDismiss }) => (\n  <Box>\n    <Text\n      fontWeight=\"400\"\n      color=\"nearBlack\"\n      fontSize={2}\n      mb={3}\n    >\n      There was an error loading that sample.\n    </Text>\n    <Box display=\"flex\" justifyContent=\"flex-end\">\n      <HoverButton\n        bg=\"blue\"\n        hoverBg=\"darkBlue\"\n        onClick={onDismiss}\n        width=\"7rem\"\n        color=\"white\"\n        display=\"block\"\n        py={2}\n      >\n        OK\n      </HoverButton>\n    </Box>\n  </Box>\n);\n\nSampleLoadError.propTypes = {\n  onDismiss: PropTypes.func.isRequired,\n};\n"
  },
  {
    "path": "src/components/SampleSelect/SampleSelect.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport Select from 'react-select';\nimport { Box, ControlLabel } from '../design-system';\nimport sampleOptions from '../../samples.config';\nimport theme from '../../styles/theme';\n\nconst openFileInput = React.createRef();\n\nconst factoryOptions = sampleOptions.map(sampleOption => ({\n  value: sampleOption.url,\n  label: sampleOption.name,\n}));\n\nconst getSampleSelectOptions = (userOptions = []) => {\n  const groupedOptions = [\n    {\n      label: 'User',\n      options: [\n        {\n          value: 'CHOOSE_FILE',\n          label: 'Choose file...',\n        },\n        ...userOptions,\n      ],\n    },\n    {\n      label: '707',\n      options: factoryOptions.filter(item => item.label.includes('707')),\n    },\n    {\n      label: '808',\n      options: factoryOptions.filter(item => item.label.includes('808')),\n    },\n    {\n      label: 'Ace',\n      options: factoryOptions.filter(item => item.label.includes('Ace')),\n    },\n    {\n      label: 'LDrum',\n      options: factoryOptions.filter(item => item.label.includes('LDrum')),\n    },\n    {\n      label: 'Hip-hop',\n      options: factoryOptions.filter(item => item.label.includes('Hip Hop')),\n    },\n  ];\n  return groupedOptions;\n};\n\nexport const SampleSelectComponent = ({\n  onSelectSample,\n  onSampleFileChosen,\n  channel,\n  userSamples,\n}) => {\n  const userOptions = userSamples.map(filename => ({\n    value: filename,\n    label: filename,\n  }));\n  const allOptions = userOptions.concat(factoryOptions);\n  const currentOption = allOptions.find(option => channel.sample === option.value);\n  return (\n    <Box>\n      <ControlLabel fontWeight=\"bold\" mb={1} ml={1} textAlign=\"left\">\n        SAMPLE\n      </ControlLabel>\n      <Select\n        aria-label=\"Select Channel\"\n        options={getSampleSelectOptions(userOptions)}\n        onChange={(choice) => {\n          if (choice.value === 'CHOOSE_FILE') {\n            openFileInput.current.click();\n          } else {\n            onSelectSample(choice);\n          }\n        }}\n        value={currentOption}\n        isSearchable={false}\n        styles={{\n          container: styles => ({\n            ...styles,\n            height: '3rem',\n          }),\n          control: styles => ({\n            ...styles,\n            backgroundColor: 'black',\n            border: `2px solid ${theme.colors.steel}`,\n            height: '100%',\n            borderRadius: '0.5em',\n          }),\n          singleValue: styles => ({\n            ...styles,\n            color: theme.colors.nearWhite,\n            opacity: channel.sampleLoaded ? 1 : 0.3,\n          }),\n          menu: styles => ({\n            ...styles,\n            fontSize: '0.8rem',\n            width: '16rem',\n          }),\n          option: styles => ({\n            ...styles,\n            paddingTop: '0.2em',\n            paddingBottom: '0.2em',\n          }),\n        }}\n      />\n      <input\n        type=\"file\"\n        ref={openFileInput}\n        style={{ display: 'none' }}\n        onChange={onSampleFileChosen}\n        accept=\"audio/*\"\n      />\n    </Box>\n  );\n};\n\nSampleSelectComponent.propTypes = {\n  onSelectSample: PropTypes.func.isRequired,\n  onSampleFileChosen: PropTypes.func.isRequired,\n  channel: PropTypes.shape({\n    sample: PropTypes.string,\n    sampleLoaded: PropTypes.bool,\n    id: PropTypes.string.isRequired,\n  }).isRequired,\n  userSamples: PropTypes.arrayOf(PropTypes.string).isRequired,\n};\n"
  },
  {
    "path": "src/components/SampleSelect/SampleSelect.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { SampleSelectComponent } from './SampleSelect.component';\nimport { saveUserSample, loadAndSetChannelSample } from '../../common';\nimport { sampleSelectSelectors } from './SampleSelect.selectors';\n\nconst mapDispatchToProps = {\n  loadAndSetChannelSample,\n  saveUserSample,\n};\n\nconst handlers = withHandlers({\n  onSelectSample: (props) => (sample) => {\n    const { loadAndSetChannelSample: connectedSetChannelSample, channel } = props;\n    connectedSetChannelSample(channel.id, sample.value);\n  },\n  onSampleFileChosen: (props) => (e) => {\n    const { saveUserSample: connectedSaveUserSample, channel } = props;\n    connectedSaveUserSample(channel.id, e.target.files);\n  },\n});\n\nexport const SampleSelect = compose(\n  connect(sampleSelectSelectors, mapDispatchToProps),\n  handlers,\n)(SampleSelectComponent);\n"
  },
  {
    "path": "src/components/SampleSelect/SampleSelect.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport {\n  channelsSelector,\n  notesSelector,\n  patternSelector,\n  selectedChannelSelector,\n  userSamplesSelector,\n} from '../../common';\n\nexport const sampleSelectSelectors = createStructuredSelector({\n  channels: channelsSelector,\n  notes: notesSelector,\n  pattern: patternSelector,\n  selectedChannelId: selectedChannelSelector,\n  userSamples: userSamplesSelector,\n});\n"
  },
  {
    "path": "src/components/SampleSelect/index.js",
    "content": "export * from './SampleSelect.container';\n"
  },
  {
    "path": "src/components/SavePresetModal/SavePresetModal.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  TextInput,\n  Text,\n  Button,\n  HoverButton,\n  Form,\n} from '../design-system';\nimport { Modal } from '../Modal.component';\nimport theme from '../../styles/theme';\n\nexport class SavePresetModalComponent extends React.Component {\n  componentDidUpdate() {\n    const { presetPromptOpen } = this.props;\n    if (presetPromptOpen) {\n      this.nameInput.focus();\n    }\n  }\n\n  render() {\n    const {\n      presetPromptOpen,\n      nameField,\n      onChangeNameField,\n      onClose,\n      onSubmit,\n      error,\n    } = this.props;\n    return (\n      <Modal show={presetPromptOpen}>\n        <Form\n          position=\"relative\"\n          display=\"flex\"\n          flexDirection=\"row\"\n          onSubmit={onSubmit}\n        >\n          <label htmlFor=\"preset-name\">\n            <Text color=\"white\" fontSize={2} height=\"2rem\">\n              Preset Name\n            </Text>\n            <TextInput\n              value={nameField}\n              onChange={onChangeNameField}\n              bg=\"white\"\n              borderRadius={3}\n              fontSize={3}\n              p={3}\n              width=\"15em\"\n              id=\"preset-name\"\n              placeholder=\"Enter name...\"\n              innerRef={(input) => { this.nameInput = input; }}\n              boxShadow={error ? `inset 0 0 0 3px ${theme.colors.lightRed}` : ''}\n            />\n          </label>\n          <HoverButton\n            bg=\"darkGray\"\n            hoverBg=\"steel\"\n            color=\"white\"\n            transitionSpeed=\"0.1s\"\n            mt=\"2rem\"\n            borderRadius={0}\n            p={0}\n            width=\"4rem\"\n            type=\"submit\"\n          >\n            SAVE\n          </HoverButton>\n          <Button\n            bg=\"transparent\"\n            color=\"gray\"\n            p={0}\n            display=\"flex\"\n            justifyContent=\"space-between\"\n            onClick={onClose}\n            fontSize={3}\n            alignItems=\"center\"\n            alignSelf=\"center\"\n            transitionSpeed=\"0.2s\"\n            position=\"absolute\"\n            right={0}\n            top={0}\n            width={20}\n            height={20}\n          >\n            <svg width=\"20\" height=\"20\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\">\n              <path d=\"M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z\" fill=\"white\" />\n            </svg>\n          </Button>\n        </Form>\n      </Modal>\n    );\n  }\n}\n\nSavePresetModalComponent.propTypes = {\n  onClose: PropTypes.func.isRequired,\n  presetPromptOpen: PropTypes.bool.isRequired,\n  onChangeNameField: PropTypes.func.isRequired,\n  onSubmit: PropTypes.func.isRequired,\n  nameField: PropTypes.string.isRequired,\n  error: PropTypes.string,\n};\n\nSavePresetModalComponent.defaultProps = {\n  error: null,\n};\n"
  },
  {
    "path": "src/components/SavePresetModal/SavePresetModal.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers, withState } from 'recompose';\nimport { SavePresetModalComponent } from './SavePresetModal.component';\nimport { savePresetModalSelectors } from './SavePresetModal.selectors';\nimport {\n  setPresetPrompt,\n  doSavePresetAs,\n} from '../../common';\nimport defaultPresets from '../../presets';\n\nconst mapDispatchToProps = {\n  setPresetPrompt,\n  doSavePresetAs,\n};\n\nconst isNameUnique = (proposedName, userPresets) => [...defaultPresets, ...userPresets].find(\n  (preset) => preset.name === proposedName,\n) === undefined;\n\nconst handlers = {\n  onChangeNameField: ({ updateNameField, setError }) => (event) => {\n    if (event.target.value.length < 32) {\n      updateNameField(event.target.value);\n      setError(null);\n    }\n  },\n  onClose: (props) => () => {\n    const {\n      setPresetPrompt: connectedSetPresetPrompt,\n      updateNameField,\n    } = props;\n    updateNameField('');\n    connectedSetPresetPrompt(false);\n  },\n  onSubmit: (props) => (event) => {\n    event.preventDefault();\n    const {\n      setPresetPrompt: connectedSetPresetPrompt,\n      doSavePresetAs: connectedDoSavePresetAs,\n      updateNameField,\n      nameField,\n      setError,\n      userPresets,\n    } = props;\n\n    if (nameField.length < 1) {\n      setError('Min length 1');\n    } else if (nameField.length > 32) {\n      setError('Max length 32');\n    } else if (!isNameUnique(nameField, userPresets)) {\n      setError('Must be unique');\n    } else {\n      connectedSetPresetPrompt(false);\n      connectedDoSavePresetAs(nameField);\n      updateNameField('');\n      setError(null);\n    }\n  },\n};\n\nexport const SavePresetModal = compose(\n  connect(savePresetModalSelectors, mapDispatchToProps),\n  withState('nameField', 'updateNameField', ''),\n  withState('error', 'setError', null),\n  withHandlers(handlers),\n)(SavePresetModalComponent);\n"
  },
  {
    "path": "src/components/SavePresetModal/SavePresetModal.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport {\n  presetPromptOpenSelector,\n  currentStateSelector,\n  userPresetsSelector,\n} from '../../common';\n\nexport const savePresetModalSelectors = createStructuredSelector({\n  userPresets: userPresetsSelector,\n  presetPromptOpen: presetPromptOpenSelector,\n  currentState: currentStateSelector,\n});\n"
  },
  {
    "path": "src/components/SavePresetModal/index.js",
    "content": "export * from './SavePresetModal.container';\n"
  },
  {
    "path": "src/components/SwingControl/SwingControl.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Knob } from '../Knob.component';\nimport { Box, Text } from '../design-system';\n\nconst LabelText = Text.extend`\n  transform: translateY(-0.3em);\n`;\n\nexport const SwingControlComponent = ({\n  onSetSwing,\n  swing,\n}) => (\n  <Box alignItems=\"center\" ml={2} display=\"flex\" flexDirection=\"column\">\n    <LabelText\n      color=\"gray\"\n      fontSize=\"0.6rem\"\n      fontWeight=\"600\"\n      bg=\"nearBlack\"\n      pl={1}\n      pr={1}\n      letterSpacing=\"0.1em\"\n    >\n      SWING\n    </LabelText>\n    <Knob size={35} onChange={onSetSwing} value={swing} min={0} max={1} step=\"0.1\" />\n  </Box>\n);\n\nSwingControlComponent.propTypes = {\n  onSetSwing: PropTypes.func.isRequired,\n  swing: PropTypes.number.isRequired,\n};\n"
  },
  {
    "path": "src/components/SwingControl/SwingControl.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose, withHandlers } from 'recompose';\nimport { SwingControlComponent } from './SwingControl.component';\nimport { swingControlSelectors } from './SwingControl.selectors';\nimport { setSwing } from '../../common';\n\nconst mapDispatchToProps = { setSwing };\n\nconst handlers = withHandlers({\n  onSetSwing: (props) => (e) => {\n    const { setSwing: setSwingConnected } = props;\n    setSwingConnected(e.target.value);\n  },\n});\n\nexport const SwingControl = compose(\n  connect(swingControlSelectors, mapDispatchToProps),\n  handlers,\n)(SwingControlComponent);\n"
  },
  {
    "path": "src/components/SwingControl/SwingControl.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport { swingSelector } from '../../common';\n\nexport const swingControlSelectors = createStructuredSelector({\n  swing: swingSelector,\n});\n"
  },
  {
    "path": "src/components/SwingControl/index.js",
    "content": "export * from './SwingControl.container';\n"
  },
  {
    "path": "src/components/Toggles/Toggle.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport styled from 'styled-components';\nimport * as ss from 'styled-system';\nimport { Box } from '../design-system';\nimport theme from '../../styles/theme';\n\nconst gradient = `linear-gradient(180deg, ${theme.colors.primary} 0%, ${theme.colors.secondary} 100%);`;\n\nconst BeatButton = styled.button`\n  ${ss.color}\n  ${ss.space}\n  ${ss.width}\n  ${ss.height}\n  ${ss.borders}\n  ${ss.borderRadius}\n  padding: 0;\n  outline: none;\n  transition: background-color 0.1s;\n  position: relative;\n  background: ${({ isActive }) => (isActive\n    ? gradient\n    : theme.colors.darkGray)}\n\n  &:focus {\n    box-shadow: 0 0 5px 5px rgba(100, 180, 255, 0.5);\n  }\n`;\n\nBeatButton.defaultProps = {\n  border: 'none',\n  borderRadius: '100%',\n};\n\nexport const Toggle = ({ isActive, onClick, beat }) => (\n  <BeatButton\n    type=\"button\"\n    isActive={isActive}\n    onClick={onClick}\n    width={[18, 18, 18, 18, 18, 20, 24, 26]}\n    height={[18, 18, 18, 18, 18, 20, 24, 26]}\n    p={0}\n    aria-label={isActive ? `disable beat ${beat}` : `enable beat ${beat}`}\n  >\n    <Box\n      className=\"wds-beat-marker\"\n      data-beat={beat}\n      data-active={isActive}\n      position=\"absolute\"\n      bg=\"white\"\n      width=\"100%\"\n      height=\"100%\"\n      left={0}\n      top={0}\n      opacity={0}\n      borderRadius=\"100%\"\n    />\n  </BeatButton>\n);\n\nToggle.propTypes = {\n  isActive: PropTypes.bool.isRequired,\n  onClick: PropTypes.func.isRequired,\n  beat: PropTypes.number.isRequired,\n};\n"
  },
  {
    "path": "src/components/Toggles/ToggleGroup.component.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Box } from '../design-system';\n\nexport const ToggleGroup = ({ children }) => (\n  <Box\n    bg=\"black\"\n    p={[2, 2, 2, 2, 2, 3]}\n    borderRadius=\"3em\"\n    flex=\"1 1 auto\"\n    mr={[2, 2, 2, 2, 2, 3]}\n    display=\"flex\"\n    justifyContent=\"space-between\"\n    alignItems=\"center\"\n  >\n    {children}\n  </Box>\n);\n\nToggleGroup.propTypes = {\n  children: PropTypes.node.isRequired,\n};\n"
  },
  {
    "path": "src/components/Toggles/Toggles.component.jsx",
    "content": "import React from 'react';\nimport * as R from 'ramda';\nimport PropTypes from 'prop-types';\nimport { Box } from '../design-system';\nimport { Toggle } from './Toggle.component';\nimport { ToggleGroup } from './ToggleGroup.component';\n\nconst isActive = (notes, beat) => notes.find(note => note.beat === beat) !== undefined;\n\nconst sixteenthNotes = R.range(0, 16);\n\nexport class TogglesComponent extends React.PureComponent {\n  render() {\n    const {\n      notes,\n      channelID,\n      toggleNote,\n      bpm,\n      playing,\n      pattern,\n    } = this.props;\n    const toggles = sixteenthNotes.map((index) => {\n      const beat = 1 + index / 4;\n      return (\n        <Toggle\n          key={index}\n          isActive={isActive(notes, beat)}\n          onClick={() => {\n            toggleNote(channelID, pattern, beat);\n          }}\n          bpm={bpm}\n          playing={playing}\n          beat={beat}\n        />\n      );\n    });\n\n    const toggleGroups = R.splitEvery(4, toggles);\n\n    return (\n      <Box display=\"flex\" flex=\"1 1 auto\" alignItems=\"center\">\n        {toggleGroups.map((toggleGroup, i) => (\n          <ToggleGroup key={i /* eslint-disable-line*/}>\n            {toggleGroup}\n          </ToggleGroup>\n        ))}\n      </Box>\n    );\n  }\n}\n\nTogglesComponent.propTypes = {\n  notes: PropTypes.arrayOf(PropTypes.object).isRequired,\n  channelID: PropTypes.string.isRequired,\n  toggleNote: PropTypes.func.isRequired,\n  bpm: PropTypes.number.isRequired,\n  playing: PropTypes.bool.isRequired,\n  pattern: PropTypes.number.isRequired,\n};\n"
  },
  {
    "path": "src/components/Toggles/Toggles.container.js",
    "content": "import { connect } from 'react-redux';\nimport { compose } from 'recompose';\nimport { TogglesComponent } from './Toggles.component';\nimport { toggleNote } from '../../common';\nimport { togglesSelectors } from './Toggles.selectors';\n\nconst mapDispatchToProps = {\n  toggleNote,\n};\n\nexport const Toggles = compose(\n  connect(togglesSelectors, mapDispatchToProps),\n)(TogglesComponent);\n"
  },
  {
    "path": "src/components/Toggles/Toggles.selectors.js",
    "content": "import { createStructuredSelector } from 'reselect';\nimport {\n  bpmSelector,\n  playingSelector,\n  patternSelector,\n} from '../../common';\n\nexport const togglesSelectors = createStructuredSelector({\n  bpm: bpmSelector,\n  playing: playingSelector,\n  pattern: patternSelector,\n});\n"
  },
  {
    "path": "src/components/Toggles/index.js",
    "content": "export * from './Toggles.container';\nexport * from './Toggle.component';\n"
  },
  {
    "path": "src/components/VolumeMeter.component.jsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { Box } from './design-system';\nimport { getVolume } from '../services/audioAnalyzer';\n\nconst DECAY = 0.95;\n\nlet prevVal = 0;\n\nexport const VolumeMeter = () => {\n  const ref = useRef();\n\n  function updateVolumeMeter() {\n    if (ref.current) {\n      const currentVolume = getVolume();\n\n      // Decay slowly\n      const isIncreasing = currentVolume > prevVal;\n      let meteredVolume;\n      if (isIncreasing) {\n        meteredVolume = currentVolume;\n      } else {\n        meteredVolume = currentVolume + (prevVal - currentVolume) * DECAY;\n      }\n      prevVal = meteredVolume;\n\n      // Convert to CSS\n      const percent = Math.min(Math.round(meteredVolume * 75), 50);\n      const color = `hsla(16, 100%, ${percent}%, 1)`;\n      ref.current.style.backgroundColor = color;\n      window.requestAnimationFrame(() => {\n        updateVolumeMeter();\n      });\n    }\n  }\n\n  useEffect(() => {\n    window.requestAnimationFrame(() => {\n      updateVolumeMeter();\n    });\n  });\n\n  return (\n    <Box\n      width={6}\n      height={6}\n      borderRadius=\"50%\"\n      bg=\"black\"\n      innerRef={ref}\n      mx={1}\n    />\n  );\n};\n"
  },
  {
    "path": "src/components/design-system/Box.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nexport const Box = styled.div`\n  ${ss.color}\n  ${ss.space}\n  ${ss.borders}\n  ${ss.borderColor}\n  ${ss.borderRadius}\n  ${ss.width}\n  ${ss.height}\n  ${ss.flex}\n  ${ss.flexDirection}\n  ${ss.display}\n  ${ss.justifyContent}\n  ${ss.opacity}\n  ${ss.position}\n  ${ss.alignItems}\n  ${ss.left}\n  ${ss.top}\n  ${ss.bottom}\n  ${ss.right}\n  ${ss.zIndex}\n  ${ss.boxShadow}\n  ${ss.maxWidth}\n  ${ss.minWidth}\n  ${ss.maxHeight}\n  ${ss.minHeight}\n  box-sizing: border-box;\n`;\n"
  },
  {
    "path": "src/components/design-system/Button.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nconst Button = styled.button`\n  ${ss.color}\n  ${ss.width}\n  ${ss.height}\n  ${ss.space}\n  ${ss.borders}\n  ${ss.borderRadius}\n  ${ss.fontWeight}\n  ${ss.fontSize}\n  ${ss.alignSelf}\n  ${ss.width}\n  ${ss.height}\n  ${ss.flex}\n  ${ss.position}\n  ${ss.left}\n  ${ss.top}\n  ${ss.bottom}\n  ${ss.right}\n  ${ss.display}\n  ${ss.alignItems}\n  ${ss.justifyContent}\n  ${ss.opacity}\n  ${ss.minWidth}\n  outline: ${({ outline }) => outline};\n  touch-action: manipulation;\n`;\n\nButton.defaultProps = {\n  border: 'none',\n  fontWeight: 'bold',\n  borderRadius: '0.25rem',\n  variant: 'primary',\n  width: 5,\n};\n\nexport { Button };\n"
  },
  {
    "path": "src/components/design-system/ControlLabel.js",
    "content": "import { Text } from './Text';\n\nexport const ControlLabel = Text.extend`\n  font-size: 0.7em;\n  text-transform: uppercase;\n  color: white;\n`;\n"
  },
  {
    "path": "src/components/design-system/Form.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nexport const Form = styled.form`\n  ${ss.color}\n  ${ss.space}\n  ${ss.borders}\n  ${ss.borderColor}\n  ${ss.borderRadius}\n  ${ss.width}\n  ${ss.height}\n  ${ss.flex}\n  ${ss.flexDirection}\n  ${ss.display}\n  ${ss.justifyContent}\n  ${ss.opacity}\n  ${ss.position}\n  ${ss.alignItems}\n  ${ss.left}\n  ${ss.top}\n  ${ss.bottom}\n  ${ss.right}\n  ${ss.zIndex}\n  box-sizing: border-box;\n`;\n"
  },
  {
    "path": "src/components/design-system/Heading.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nexport const Heading = styled.h1`\n  ${ss.color}\n  ${ss.fontSize}\n  ${ss.fontWeight}\n  ${ss.space}\n  ${ss.fontFamily}\n`;\n"
  },
  {
    "path": "src/components/design-system/HoverButton.js",
    "content": "import { Button } from './Button';\n\nconst getColor = (theme, color) => theme.colors[color] || color;\n\nexport const HoverButton = Button.extend`\n  transition: all ${({ transitionSpeed }) => transitionSpeed}\n\n  &:hover {\n    color: ${({ theme, hoverColor }) => getColor(theme, hoverColor)};\n    background-color: ${({ theme, hoverBg }) => getColor(theme, hoverBg)};\n    opacity: ${({ hoverOpacity }) => hoverOpacity};\n  }\n\n  &:active {\n    background-color: ${({ theme, activeBg }) => getColor(theme, activeBg)};\n    opacity: ${({ hoverOpacity }) => hoverOpacity};\n  }\n`;\n"
  },
  {
    "path": "src/components/design-system/HoverLink.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nexport const HoverLink = styled.a`\n  ${ss.opacity}\n  text-decoration: none;\n  display: inline-block;\n  transition: all ${({ transitionSpeed }) => transitionSpeed};\n\n  &:hover, &:focus {\n    opacity: ${({ hoverOpacity }) => hoverOpacity};\n  }\n\n  &:active {\n    opacity: ${({ activeOpacity }) => activeOpacity};\n  }\n`;\n"
  },
  {
    "path": "src/components/design-system/Image.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nexport const Image = styled.img`\n  ${ss.color}\n  ${ss.space}\n  ${ss.width}\n  ${ss.height}\n  ${ss.flex}\n  ${ss.display}\n  ${ss.justifyContent}\n  ${ss.opacity}\n  ${ss.position}\n  user-select: ${({ userSelect }) => userSelect};\n`;\n"
  },
  {
    "path": "src/components/design-system/Label.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nconst Label = styled.label`\n  ${ss.color}\n  ${ss.fontWeight}\n  ${ss.fontSize}\n  ${ss.space}\n  ${ss.position}\n  ${ss.left}\n  ${ss.top}\n  ${ss.letterSpacing}\n  ${ss.height}\n  display: block;\n  line-height: 1em;\n`;\n\nLabel.defaultProps = {\n  m: 0,\n  p: 0,\n};\n\nexport { Label };\n"
  },
  {
    "path": "src/components/design-system/Line.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nconst Line = styled.div`\n  ${ss.color}\n  ${ss.space}\n  ${ss.borderRadius}\n  ${ss.width}\n  ${ss.height}\n  ${ss.flex}\n  ${ss.display}\n  ${ss.opacity}\n  ${ss.position}\n  ${ss.alignItems}\n`;\n\nLine.defaultProps = {\n  bg: 'nearWhite',\n  width: '100%',\n  display: 'block',\n  height: 1,\n};\n\nexport { Line };\n"
  },
  {
    "path": "src/components/design-system/Text.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nconst Text = styled.span`\n  ${ss.color}\n  ${ss.fontWeight}\n  ${ss.fontSize}\n  ${ss.space}\n  ${ss.position}\n  ${ss.left}\n  ${ss.top}\n  ${ss.letterSpacing}\n  ${ss.height}\n  ${ss.zIndex}\n  ${ss.borderRadius}\n  ${ss.textAlign}\n  ${ss.opacity}\n  ${ss.lineHeight}\n  ${ss.display}\n  ${ss.verticalAlign}\n  user-select: ${({ userSelect }) => userSelect};\n`;\n\nText.defaultProps = {\n  m: 0,\n  p: 0,\n  lineHeight: '1em',\n  display: 'block',\n};\n\nexport { Text };\n"
  },
  {
    "path": "src/components/design-system/TextInput.js",
    "content": "import styled from 'styled-components';\nimport * as ss from 'styled-system';\n\nconst TextInput = styled.input`\n  ${ss.color}\n  ${ss.fontWeight}\n  ${ss.fontSize}\n  ${ss.space}\n  ${ss.position}\n  ${ss.zIndex}\n  ${ss.width}\n  ${ss.height}\n  ${ss.boxShadow}\n  display: block;\n  border: none;\n`;\n\nTextInput.defaultProps = {\n  m: 0,\n  p: 0,\n};\n\nexport { TextInput };\n"
  },
  {
    "path": "src/components/design-system/index.js",
    "content": "export * from './Heading';\nexport * from './Box';\nexport * from './Button';\nexport * from './HoverButton';\nexport * from './Text';\nexport * from './Line';\nexport * from './Image';\nexport * from './TextInput';\nexport * from './Form';\nexport * from './HoverLink';\nexport * from './Label';\nexport * from './ControlLabel';\n"
  },
  {
    "path": "src/components/index.js",
    "content": "export * from './design-system';\nexport * from './ChannelList';\nexport * from './Channel';\nexport * from './ChannelHeader';\nexport * from './Toggles';\nexport * from './PlayButton';\nexport * from './BPMInput';\nexport * from './Marker';\nexport * from './FancyButton.component';\nexport * from './AddChannelButton';\nexport * from './PresetSelector';\nexport * from './MasterControls';\nexport * from './Modal.component';\nexport * from './Logo.component';\nexport * from './GithubLink.component';\nexport * from './ChannelControls';\nexport * from './FlashMessage';\nexport * from './SwingControl';\nexport * from './Branding';\nexport * from './InstallButton';\n"
  },
  {
    "path": "src/components/timedCallback.hoc.jsx",
    "content": "import React from 'react';\n\nexport const timedCallback = (callback, delay) => WrappedEl => class extends React.Component {\n  constructor() {\n    super();\n    this.timer = setTimeout(callback, delay);\n  }\n\n  componentWillUnmount() {\n    clearTimeout(this.timer);\n  }\n\n  render() {\n    return <WrappedEl {...this.props} />;\n  }\n};\n"
  },
  {
    "path": "src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <!-- Global site tag (gtag.js) - Google Analytics -->\n    <script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-66781633-3\"></script>\n    <script>\n      window.dataLayer = window.dataLayer || [];\n      function gtag() { dataLayer.push(arguments); }\n      gtag('js', new Date());\n\n      gtag('config', 'UA-66781633-3');\n    </script>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <meta name=\"Description\" content=\"Browser-based drum machine and sequencer for crafting beats on the go. You can save your creations and work offline.\">\n    <title>WDS-1: Web Drum Sequencer</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <noscript>\n      You need to enable JavaScript to run this app.\n    </noscript>\n  </body>\n</html>\n"
  },
  {
    "path": "src/index.jsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { PersistGate } from 'redux-persist/integration/react';\nimport App from './components/App';\nimport { initializeAudio } from './services/audioLoop';\nimport { configureStore } from './store';\nimport { loadSampleStatefully } from './common';\nimport { startAnimations } from './services/animations';\nimport { initializePwaInstall } from './services/pwaInstall';\nimport { initializeDB } from './services/database';\n\nconst { store, persistor } = configureStore();\n\n/**\n * Watch for user going online, and try to load any samples\n * that haven't been loaded (e.g. because user was offline)\n */\nwindow.addEventListener('online', () => {\n  const { channels } = store.getState();\n  channels.forEach((channel) => {\n    if (!channel.sampleLoaded) {\n      loadSampleStatefully(store.dispatch, channel);\n    }\n  });\n});\n\n// Register service worker\nif (import.meta.env.DEV && 'serviceWorker' in navigator) {\n  try {\n    navigator.serviceWorker.register('./sw.js', {\n      scope: '/',\n    });\n  } catch (error) {\n    console.error(`Registration failed with ${error}`);\n  }\n}\n\nReactDOM.render(\n  <Provider store={store}>\n    <PersistGate loading={null} persistor={persistor}>\n      <App />\n    </PersistGate>\n  </Provider>,\n  document.getElementById('root'),\n);\n\ninitializeAudio(store);\n\nstartAnimations(store);\n\ninitializePwaInstall(store);\n\ninitializeDB().then(() => {\n  const { channels } = store.getState();\n  // Load up all the initial samples\n  channels.forEach((channel) => {\n    loadSampleStatefully(store.dispatch, channel);\n  });\n});\n"
  },
  {
    "path": "src/presets/707.js",
    "content": "import { samples } from '../samples.config';\n\nexport default {\n  name: '707',\n  bpm: 133,\n  swing: 0.2,\n  channels: [\n    {\n      id: 'empty_channel',\n      sample: samples.tr707Bd,\n      gain: 1,\n    },\n    {\n      id: '19a36a53-a907-41f2-baef-394f0fbf0403',\n      sample: samples.tr707SdHigh,\n      gain: 1,\n    },\n    {\n      id: 'ca0d0d5f-61af-45a4-8d36-8648d20e3132',\n      sample: samples.tr707Ch,\n      gain: 0.19,\n    },\n    {\n      id: '69aa5d1a-085a-4083-93c5-b3aa7a334278',\n      sample: samples.tr707Oh,\n      gain: 0.32,\n    },\n    {\n      id: '5a2f4293-159f-46e9-a9de-52eaeb326483',\n      sample: samples.tr707Clap,\n      gain: 0.75,\n    },\n    {\n      id: '1449a5d5-79ea-4332-83e0-d5513a82d16f',\n      sample: samples.tr707Tamb,\n      gain: 0.37,\n    },\n  ],\n  notes: {\n    empty_channel: [\n      [\n        {\n          beat: 1,\n          id: '79773a84-0861-4c5f-8377-17a103e953f5',\n        },\n        {\n          beat: 2.5,\n          id: '896a049b-288a-4441-850c-f364106345c1',\n        },\n        {\n          beat: 3.5,\n          id: '27865470-1309-4439-88fc-141c425a0954',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'fdbc7ee8-b1e6-49fa-8ce9-7acc3ad86d35',\n        },\n        {\n          beat: 3.5,\n          id: '90c9541e-3d94-4f4f-885a-a362bca2e973',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'e81aee0f-87e5-48d1-abdd-d86318fc1943',\n        },\n        {\n          beat: 1.75,\n          id: 'f5b70c60-ad16-4537-b436-4f05f1e5da6c',\n        },\n        {\n          beat: 2.5,\n          id: '1046df31-74c8-4f3e-8307-68f7933eee8b',\n        },\n        {\n          beat: 3.5,\n          id: 'f4871aa7-b265-4f9b-bfd4-7622a20ef9ca',\n        },\n        {\n          beat: 4.5,\n          id: '1b3d4594-fd4e-4d88-ba9f-72018b6130db',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '21a840df-bbf6-4d66-be0f-2f9c779af3a8',\n        },\n        {\n          beat: 4.5,\n          id: 'eed92161-5fde-42ac-b2a1-12c9f4cf8490',\n        },\n        {\n          beat: 2.5,\n          id: '0721650c-59f6-41c6-81dd-b426ab07bbe5',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '131ee00c-23a9-4acc-bc90-6aac8b5575b1',\n        },\n        {\n          beat: 3,\n          id: '081ea16f-ec75-48cf-9867-33116a3ff052',\n        },\n        {\n          beat: 3.75,\n          id: 'afa06799-5e9a-4289-81e2-350ddfd0470d',\n        },\n        {\n          beat: 4.5,\n          id: 'ad7dfa99-402b-4398-8d4c-d7263194b635',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'd96dc48b-b2fb-4e3c-9b8d-8c41b4559885',\n        },\n        {\n          beat: 1.25,\n          id: 'e399b29b-7ff8-4212-a122-1564cbe347a9',\n        },\n        {\n          beat: 2.75,\n          id: '4ed059cf-f917-458f-a9ac-ad41ddca64ab',\n        },\n        {\n          beat: 2.5,\n          id: 'ae266807-6e79-4cda-9b82-7b45b5ccc3cf',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '5d85f763-3650-4bfb-9e02-e0e51a2dd543',\n        },\n        {\n          beat: 3.5,\n          id: 'dc87df2e-0b5b-4668-acbe-0d1f043e95c8',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '921e81f6-5f69-4b0e-a2d4-4f8270f150de',\n        },\n        {\n          beat: 1.75,\n          id: '736030e0-a968-4062-828d-9ffe54bcfd93',\n        },\n        {\n          beat: 2.5,\n          id: 'd4d5a045-e563-4495-b824-c7fdf91ac10d',\n        },\n        {\n          beat: 3.5,\n          id: '9a64edf9-c4d0-4ab5-b710-61ac068d4aa3',\n        },\n        {\n          beat: 4.25,\n          id: 'afdaf0d4-8735-4a76-b158-c7f67eb5170e',\n        },\n      ],\n    ],\n    'd65e1669-fbce-49ee-8bfc-dc7928407385': [\n      [],\n      [\n        {\n          beat: 2,\n          id: '8f290791-dc31-4b76-a536-041af5a1d495',\n        },\n        {\n          beat: 4,\n          id: 'ab838c5e-d3e0-4e18-a6e8-44a309b965d4',\n        },\n      ],\n      [],\n      [],\n      [],\n      [],\n      [],\n      [],\n    ],\n    '19a36a53-a907-41f2-baef-394f0fbf0403': [\n      [\n        {\n          beat: 2,\n          id: 'b2c8970e-9790-40b2-862e-6abca6648d7d',\n        },\n        {\n          beat: 4,\n          id: '540ab903-42e1-401e-b0ef-9fa131cd81de',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '21e78843-24c7-49c9-80cf-e24fb9cd1b17',\n        },\n        {\n          beat: 4,\n          id: 'afc253e4-4669-4992-b37d-bb620bc94992',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '5db6f04b-04d9-4f7b-93c6-64b0501e4e78',\n        },\n        {\n          beat: 4,\n          id: 'db751285-c481-46fd-82b1-d5d6dadce699',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '7ee58927-5006-44ff-b570-44956a5c34af',\n        },\n        {\n          beat: 3.5,\n          id: '658b91d8-05c8-4228-93f5-68b35a56790b',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '49a9078d-3663-4ca7-b264-6ae3d2b3d25b',\n        },\n        {\n          beat: 4,\n          id: 'a6a1fd44-312f-49bb-a023-4ceaf8cad4a2',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '1fc7662d-ed97-42c3-bb32-4b4a61a98c0c',\n        },\n        {\n          beat: 4,\n          id: 'c7902b4a-23b8-4ef9-afe8-7fee06fe86de',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2,\n          id: '68364efb-1515-41b9-af10-062e11effca7',\n        },\n        {\n          beat: 3,\n          id: '60271671-559f-4f4c-9b92-69814e908bf4',\n        },\n        {\n          beat: 3.25,\n          id: '9e3cfbff-b83f-4f51-871b-18dc225f6020',\n        },\n        {\n          beat: 3.75,\n          id: '373b4464-8a24-4495-955f-c7998677e054',\n        },\n        {\n          beat: 4.5,\n          id: 'b7e45875-7453-4a6f-8440-4e0374b66ae3',\n        },\n      ],\n    ],\n    'ca0d0d5f-61af-45a4-8d36-8648d20e3132': [\n      [\n        {\n          beat: 1,\n          id: '49dcbbcf-7d18-4ab1-ad39-248fe4ff62c6',\n        },\n        {\n          beat: 2,\n          id: 'd59f046b-a64c-4db8-b6f0-d50f7affe89c',\n        },\n        {\n          beat: 3,\n          id: 'aab9a08c-c0c7-4f04-94b5-1fd0e45fb820',\n        },\n        {\n          beat: 4,\n          id: '543315bb-a931-4754-8f68-bdb2765b88f0',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: '83e7d304-0dc5-4089-80ba-50ba20f918d9',\n        },\n        {\n          beat: 2.5,\n          id: 'd00d0a86-75e3-4dc5-98e2-7f92eef51103',\n        },\n        {\n          beat: 3.5,\n          id: '746e4b6b-b9cf-4c68-903e-76da267d05d7',\n        },\n        {\n          beat: 4.5,\n          id: '723ebc55-f518-43ca-bf47-85fd4b1b9c9c',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '208ea2bd-4970-4134-a2e4-e0276d4f5455',\n        },\n        {\n          beat: 2,\n          id: 'ff4d906f-9e2a-437a-91c2-cf9a8e8f80f0',\n        },\n        {\n          beat: 3,\n          id: '0da187a0-bf27-41f2-9ffa-70b586bb3d8c',\n        },\n        {\n          beat: 4,\n          id: 'af8ff590-9759-4ec0-9491-25cbaf23416d',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '61702e67-426e-4084-a762-eedfbab99739',\n        },\n        {\n          beat: 3,\n          id: 'af1567ab-0b9c-49dd-8fde-284086dae025',\n        },\n        {\n          beat: 1,\n          id: '05a9287e-4ef1-4e3e-a7ba-35c822ccc696',\n        },\n        {\n          beat: 4,\n          id: 'a229b9b8-fcef-4f65-820c-f02a08614ff1',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'edc9fc6a-2bb6-4217-9df8-bc283cec4207',\n        },\n        {\n          beat: 1.25,\n          id: 'bf9b5e2b-9b79-4e22-9a66-01351045306a',\n        },\n        {\n          beat: 1.5,\n          id: 'a206bcc9-5848-4aca-b6c4-fc619b88a06a',\n        },\n        {\n          beat: 1.75,\n          id: '2312e7d0-aa40-4bdc-bd45-09345652aa3f',\n        },\n        {\n          beat: 2,\n          id: '57e63bad-6765-4eb3-8e98-cb1de36d9eab',\n        },\n        {\n          beat: 2.25,\n          id: '68b2d4b2-9f04-4f0b-894e-17f74719ed7d',\n        },\n        {\n          beat: 2.5,\n          id: '3b0bcaff-1f8e-4bbf-b2e6-693d2f5000c5',\n        },\n        {\n          beat: 2.75,\n          id: 'b010de6a-01ea-46c2-8c21-62d2eaaeb2ca',\n        },\n        {\n          beat: 3,\n          id: 'a2880e10-c4d5-43be-be5a-4c1aae9e929f',\n        },\n        {\n          beat: 3.25,\n          id: 'f88dd5f3-8d35-40f2-b818-69415072bdce',\n        },\n        {\n          beat: 3.5,\n          id: '8766fc70-2160-42e1-8e53-229c005e305e',\n        },\n        {\n          beat: 3.75,\n          id: 'a18c7d1e-238c-480c-bfb0-626df60bee9c',\n        },\n        {\n          beat: 4,\n          id: '37e1c631-6abb-4044-9400-149005de63ce',\n        },\n        {\n          beat: 4.25,\n          id: 'd0308433-5217-4371-bd7a-e353da21aaa7',\n        },\n        {\n          beat: 4.75,\n          id: 'f255ca9c-b6be-468b-95a1-7b2b6cc48a1a',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '32d56fe7-a68c-45ce-b1e4-8f47e377e7ba',\n        },\n        {\n          beat: 2,\n          id: '924a0f29-1876-4b5d-9750-84146cc7b3de',\n        },\n        {\n          beat: 4,\n          id: 'cde3d121-02c9-4376-b60f-c13965fc9e77',\n        },\n        {\n          beat: 3,\n          id: 'b2171ad3-8d5e-473f-82be-2d9b9dfc1fbc',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'e54c169d-999e-4c5b-b860-1a077ef1b89e',\n        },\n        {\n          beat: 1.75,\n          id: '6379021f-bf80-45cd-a5b0-96d0172a6992',\n        },\n        {\n          beat: 2.5,\n          id: 'b9ff3cfa-eb1e-4b4a-8947-64c1ced9912a',\n        },\n        {\n          beat: 3,\n          id: 'd12eab75-cc84-4cea-adbd-a48cab39fb90',\n        },\n        {\n          beat: 3.25,\n          id: '8535ac71-ef73-4b4a-a832-2a9253ffa050',\n        },\n        {\n          beat: 3.75,\n          id: '067adfda-0c10-4eef-af41-13344599c979',\n        },\n        {\n          beat: 4.25,\n          id: '511d9a2a-bd48-4aa1-b8e7-b4861f5d53dd',\n        },\n        {\n          beat: 4.5,\n          id: '6fd510b6-e966-4e9e-b0ce-754de3f5de80',\n        },\n      ],\n    ],\n    '69aa5d1a-085a-4083-93c5-b3aa7a334278': [\n      [],\n      [\n        {\n          beat: 4.5,\n          id: 'b3b987ad-53cf-4fac-8585-95e70b02b8bb',\n        },\n      ],\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 4.5,\n          id: '8ba64f81-82c5-4d1a-94fe-7c7b2a8d618f',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2,\n          id: '4b588776-4189-4642-897a-b49f4bd5d825',\n        },\n      ],\n    ],\n    '5a2f4293-159f-46e9-a9de-52eaeb326483': [\n      [\n        {\n          beat: 4,\n          id: 'bf626801-b8b7-4ed9-b8d4-25bcb3197a9a',\n        },\n        {\n          beat: 2.5,\n          id: '09d3f172-bae4-4c02-bab5-f5c52c9d5944',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '470249a0-7783-4129-95e5-0e44cf3ef520',\n        },\n        {\n          beat: 3.5,\n          id: '53a4d4f3-dcd1-41a6-9964-61d66b0ce4d9',\n        },\n        {\n          beat: 4,\n          id: 'c82e8cb2-5bb6-4ad1-85a2-f54d0a9f5aa7',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2,\n          id: '979fd4bc-1c06-462d-887c-f2c67f710a38',\n        },\n        {\n          beat: 4,\n          id: 'c8f0619e-6577-47b6-9ca6-c1cf08348702',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'a56f2267-1086-45a4-891f-ed20b0a8b830',\n        },\n        {\n          beat: 4,\n          id: '85566726-c759-4b3f-9c98-cab677c81b24',\n        },\n      ],\n      [\n        {\n          beat: 2.5,\n          id: '9d78a8e3-95c5-42a8-8b04-b13fa221c9fc',\n        },\n        {\n          beat: 4,\n          id: '4fb62413-2073-4e0a-8e83-9ee507743185',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'bc12ec84-7d10-4478-8702-e8b9694e9f90',\n        },\n        {\n          beat: 4,\n          id: '3b6c3ce3-61e6-4486-bd3a-091e6ec4b61f',\n        },\n      ],\n      [\n        {\n          beat: 3,\n          id: '118885eb-9f4e-4f75-b974-be31bfc88b23',\n        },\n        {\n          beat: 4,\n          id: '03112e7d-c9a3-43e9-81a0-b3868ba23429',\n        },\n      ],\n    ],\n    '1449a5d5-79ea-4332-83e0-d5513a82d16f': [\n      [],\n      [\n        {\n          beat: 1,\n          id: '40d7c694-19cb-4671-98d6-e0cbd128ad8b',\n        },\n        {\n          beat: 1.5,\n          id: 'd2d07448-5c59-472a-ab8c-a12222b042b6',\n        },\n        {\n          beat: 2,\n          id: '5c6b8efb-0f18-44d9-ba93-305bc5c04eb4',\n        },\n        {\n          beat: 2.5,\n          id: 'bb5d511d-febc-4557-a658-e3bbb6f743cf',\n        },\n        {\n          beat: 3,\n          id: 'a5d5cd55-516c-4307-a946-45ac8043899a',\n        },\n        {\n          beat: 3.5,\n          id: '23501c3c-97ac-4ceb-819f-dd1076c278ca',\n        },\n        {\n          beat: 4,\n          id: '299a7e10-d73a-47b3-9f82-fbc8061f3b39',\n        },\n        {\n          beat: 4.5,\n          id: '0c981e72-048e-42c8-9c67-73d12d3e0acf',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '454dafea-f810-454e-970c-d046239f2a4e',\n        },\n        {\n          beat: 3,\n          id: 'c952921a-fee1-4221-9a54-1b2031ae854d',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '29da9814-96af-4c98-a0dd-f5e655a808b3',\n        },\n        {\n          beat: 4,\n          id: '3a12f5b2-7cf1-44f5-848b-eeaf5b14a9d5',\n        },\n        {\n          beat: 3,\n          id: 'e0046def-f622-4d39-a951-91aab5896818',\n        },\n        {\n          beat: 2,\n          id: '2e0c328a-4ddd-4109-bac6-eb6d9a19f8e9',\n        },\n      ],\n      [\n        {\n          beat: 3,\n          id: '53c354e4-f8e7-4ad1-a4e4-56b91130d599',\n        },\n        {\n          beat: 1,\n          id: 'cd8b58cc-e634-449a-bcf7-59e2a7ab52af',\n        },\n        {\n          beat: 2,\n          id: 'bf82ff02-f111-4ab3-aef4-41a432678129',\n        },\n        {\n          beat: 4,\n          id: '6344d678-2281-4595-b397-fcb31efc7a91',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '9d87a6f8-ce7f-480c-8005-9bf306d06e4f',\n        },\n        {\n          beat: 3.5,\n          id: 'cf323090-5b13-4d8b-a923-a3e05dbc608d',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'ee95c968-439d-4f0a-bfd0-6652d70e2759',\n        },\n      ],\n    ],\n  },\n};\n"
  },
  {
    "path": "src/presets/808.js",
    "content": "import { samples } from '../samples.config';\n\nexport default {\n  name: '808',\n  bpm: 105,\n  swing: 0,\n  channels: [\n    {\n      id: 'empty_channel',\n      sample: samples.tr808BdShort,\n      gain: 0.83,\n    },\n    {\n      id: 'eb405dca-32d1-4867-b7e0-ef37198f5fee',\n      sample: samples.tr808BdLong,\n      gain: 1,\n    },\n    {\n      id: '59fe4d57-c587-441b-b10d-9a2b54d588fd',\n      sample: samples.tr808Sd,\n      gain: 1,\n    },\n    {\n      id: '6a2a5e54-8cbc-4e7e-a81c-68fc4b34c0bc',\n      sample: samples.tr808Clap,\n      gain: 0.85,\n    },\n    {\n      id: '9b6de154-8a6e-4941-80db-4b9f47d9c241',\n      sample: samples.tr808Ch,\n      gain: 0.21,\n    },\n    {\n      id: 'fb2edb2c-3d21-49fd-98d8-459b153fb083',\n      sample: samples.tr808Oh,\n      gain: 0.28,\n    },\n    {\n      id: '27f949f4-f5f7-4846-abfb-1d0e544b5bff',\n      sample: samples.tr808Cowbell,\n      gain: 0.55,\n    },\n    {\n      id: 'd8d65425-e980-43a6-b3b3-1c14b55b8c94',\n      sample: samples.tr808Cym,\n      gain: 0.37,\n    },\n    {\n      id: 'dbbfd24a-0f5d-409f-b4ed-ec330b47e2e5',\n      sample: samples.tr808Clav,\n      gain: 0.31,\n    },\n    {\n      id: '206fcf08-67ff-4e98-905e-2495383fb83c',\n      sample: samples.tr808Rs,\n      gain: 0.43,\n    },\n    {\n      id: '0ea2f943-2144-4434-92f7-8d689d49633a',\n      sample: samples.tr808Ht,\n      gain: 0.71,\n    },\n    {\n      id: '2f090e95-37f7-4c49-b0c5-eb35d64216c8',\n      sample: samples.tr808Mt,\n      gain: 0.59,\n    },\n    {\n      id: '816f7250-0afb-4353-bc6c-84ad97f9b8c6',\n      sample: samples.tr808Lt,\n      gain: 0.71,\n    },\n  ],\n  notes: {\n    empty_channel: [\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '8d680b7a-2d25-4e13-867f-8b6b8b6415b0',\n        },\n        {\n          beat: 3,\n          id: '420a2676-67a4-4006-a068-22cdb19fb5ac',\n        },\n        {\n          beat: 4,\n          id: '91ffc7b1-ef92-452e-99df-02ce05d01277',\n        },\n        {\n          beat: 2,\n          id: 'a044d8e8-9a4a-4221-8f81-071897fb94a9',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 3.5,\n          id: 'b32de0b1-4eb6-484f-8cb8-b6550bf7d5ff',\n        },\n        {\n          beat: 4.25,\n          id: '5f017b71-3a67-4735-83b0-b4dc5eb0e55d',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '5c7e76ca-fdf3-4f39-8379-d847298dcbe8',\n        },\n        {\n          beat: 2,\n          id: '9630e240-98a3-482b-a956-1b1cbbc3f6f7',\n        },\n        {\n          beat: 3,\n          id: '64f339ef-5a10-4b23-8dfb-ad68c6b8a3dc',\n        },\n        {\n          beat: 4,\n          id: '0471ef77-08b4-4d5a-815b-3a7b2541513a',\n        },\n      ],\n      [],\n    ],\n    'eb405dca-32d1-4867-b7e0-ef37198f5fee': [\n      [\n        {\n          beat: 1,\n          id: '29ddd774-51b9-47eb-aa90-15b5647af688',\n        },\n        {\n          beat: 2.5,\n          id: '97fc68b1-722d-4f86-bd01-bb7dca96d6e5',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'fd0bfd6d-8faa-4e78-9d7c-d3c176a0c2fc',\n        },\n        {\n          beat: 3.5,\n          id: 'fe3b3f20-fa95-4204-86ae-eb30e63f8b49',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '032ecd95-d72e-426d-a742-a5deb4bcfb88',\n        },\n        {\n          beat: 3.5,\n          id: '29082399-8b29-4ce7-829c-6ae8d15da51c',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '86b85f42-dcdb-44a8-a0eb-425d4bf97ffa',\n        },\n        {\n          beat: 2.5,\n          id: '15f8b82c-df03-41e8-ba72-4087c1c90663',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '6fc5428e-80b6-4d11-a3bd-42aefb1230f6',\n        },\n      ],\n    ],\n    '59fe4d57-c587-441b-b10d-9a2b54d588fd': [\n      [\n        {\n          beat: 4,\n          id: '4e31c684-0f4c-47f1-a891-50709c3bf1fc',\n        },\n        {\n          beat: 2,\n          id: '64743cf2-27a8-4ad6-af11-621f5b2acdfa',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '4600798b-3b4a-4465-a494-4a071d9d4d84',\n        },\n        {\n          beat: 4,\n          id: '32205ee0-e933-493f-af20-eece6f97e2ab',\n        },\n      ],\n      [\n        {\n          beat: 4.75,\n          id: '25531c10-3a3f-425a-8814-7800830a2cde',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '8c3a23f5-e6d8-458c-92de-c1f92be0f0f9',\n        },\n        {\n          beat: 4,\n          id: 'b3af60f9-8a57-4a00-ac9b-0175eafffc77',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '88e1fa22-f678-4cd2-926b-296cd1a09948',\n        },\n        {\n          beat: 4,\n          id: '5dab556e-f303-4ed2-b85c-9ec47dbf026d',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'c0ed62e3-2763-4bf5-b80c-58aa5154cf99',\n        },\n        {\n          beat: 4,\n          id: '95355109-8f8e-4964-b44d-831a6c3f1433',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 3.5,\n          id: '3cf162e3-5d2d-4ff9-b2b6-6aa372280ea8',\n        },\n        {\n          beat: 4.5,\n          id: '401eeb6f-ec89-4b57-b687-9d87b0f7d04f',\n        },\n        {\n          beat: 4.75,\n          id: '9ddab637-94c1-438e-9ec7-331df982d034',\n        },\n        {\n          beat: 4,\n          id: '0c0b94fe-ceda-424b-8d8b-7c717f13ec01',\n        },\n      ],\n    ],\n    '6a2a5e54-8cbc-4e7e-a81c-68fc4b34c0bc': [\n      [\n        {\n          beat: 2,\n          id: '0b7163d9-dfdc-4e0c-b5b0-f7730c198f54',\n        },\n        {\n          beat: 4,\n          id: '0ec12d1a-8ed9-4f44-99d4-d2a9cbc8cd99',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '4a11b3b3-81c2-4bd8-8b27-de87dabe02ac',\n        },\n        {\n          beat: 2.75,\n          id: '71160d69-5717-4369-99bf-e15dcf4c7bc6',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '0fc99548-9184-4ff0-8531-cbec8a5c11c2',\n        },\n        {\n          beat: 4,\n          id: '78b6b419-5274-4b35-a1aa-d05bed345509',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2.75,\n          id: '8b8d953f-3345-40af-90ce-3d1f786f7bdb',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '2e01cf3a-15c4-4693-990d-c237c5691c82',\n        },\n        {\n          beat: 4,\n          id: 'af6bc3b9-479c-4efe-b06a-355a44991302',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'd389f8c8-565c-4d31-a9bb-ca3f72cb8a07',\n        },\n        {\n          beat: 4,\n          id: 'ab33470e-2ee2-421b-9541-e0fe340f32a6',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '9ad61605-2b9c-407b-aa82-45972b2bd4aa',\n        },\n        {\n          beat: 4,\n          id: '5c475e3c-8487-4d0d-8ec0-d3afacaaa556',\n        },\n      ],\n    ],\n    '9b6de154-8a6e-4941-80db-4b9f47d9c241': [\n      [\n        {\n          beat: 1,\n          id: 'ea650f53-6330-492d-a92e-47ad46e49c8b',\n        },\n        {\n          beat: 1.5,\n          id: '76e109d2-7a93-475a-a1ca-e635262e824b',\n        },\n        {\n          beat: 2,\n          id: 'be5d8b74-1d00-4ce7-a075-1f4770bdeb04',\n        },\n        {\n          beat: 2.5,\n          id: '09aa3eba-d22b-4987-b1bd-07d796b055e1',\n        },\n        {\n          beat: 3,\n          id: '9d4f42eb-ad56-45ea-a183-6034047786fa',\n        },\n        {\n          beat: 4,\n          id: 'd151c78e-dee5-4e3a-bb08-d8916c226d70',\n        },\n        {\n          beat: 3.5,\n          id: '2dce2fae-5413-40ef-9192-49d00dee623c',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '2942f843-440c-46e9-aa5e-8cebee9e86f3',\n        },\n        {\n          beat: 2.5,\n          id: '1598985e-fbda-4325-9df7-4e4fdd426061',\n        },\n        {\n          beat: 3.5,\n          id: '01140c00-7e38-4c47-bb2f-866c87e9c2bf',\n        },\n        {\n          beat: 4.5,\n          id: 'c8eb2e22-36b7-4891-96a4-75baaefe1cd8',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '7d6d2ef5-50e6-4d25-b583-416ea1b67a73',\n        },\n        {\n          beat: 1,\n          id: '955cb6a2-05a1-4c72-9436-2c9da602f3a0',\n        },\n        {\n          beat: 2,\n          id: 'e9cbcf6f-acde-4cb4-a5c7-334fcf2f9854',\n        },\n        {\n          beat: 2.5,\n          id: '472ac95d-e5a3-4938-84cc-e6b8204833bf',\n        },\n        {\n          beat: 2.75,\n          id: '5f69d201-c2d9-4051-83bf-34fd4c5346e4',\n        },\n        {\n          beat: 3.5,\n          id: 'bdf0b3dc-cfe4-4384-84e1-80a390f6e165',\n        },\n        {\n          beat: 4,\n          id: '3a1d6250-7a3e-4bea-93fe-cc1f0015534c',\n        },\n        {\n          beat: 4.5,\n          id: 'ddb60cd9-b9e3-4bf5-810c-2824ef1241ba',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '2c2fb20d-c409-4fee-bc44-e35e9b494cc3',\n        },\n        {\n          beat: 2.5,\n          id: '613f728c-16ff-4d4b-9f74-84d2923ad13d',\n        },\n        {\n          beat: 3.5,\n          id: 'f1a762f3-4481-44d3-9b31-ae3d488c54c6',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '6edb0a4b-c91f-42dc-bb43-2e54eee77288',\n        },\n        {\n          beat: 1.5,\n          id: 'ae2f66d3-100d-431c-a3fb-c7c8ddd1c2f3',\n        },\n        {\n          beat: 2,\n          id: 'f0543739-d954-435c-a5fe-93d72ee582f2',\n        },\n        {\n          beat: 3,\n          id: 'a2edccea-0cf4-4947-b149-4191f07bdf1e',\n        },\n        {\n          beat: 3.5,\n          id: 'cee93951-856d-4093-9cea-2f3b516f792d',\n        },\n        {\n          beat: 4,\n          id: '4c604170-949c-4ff9-8789-01bd72d55f46',\n        },\n        {\n          beat: 4.5,\n          id: 'a4f3ad08-12bd-4e5e-9a2a-1f0e0b53b4d5',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'c4ef6a9a-bd43-4878-8753-3a4ef5813266',\n        },\n        {\n          beat: 1.25,\n          id: 'af3c946e-b413-4dfd-9a8e-cc9277edd88c',\n        },\n        {\n          beat: 1.5,\n          id: '3c60b720-f348-445e-a9e3-74a5ac94c9bf',\n        },\n        {\n          beat: 1.75,\n          id: '36f75f4b-12ad-4f10-a563-f243cdac6492',\n        },\n        {\n          beat: 2,\n          id: '14582379-11b5-4496-ac28-ef2fc9ff92c8',\n        },\n        {\n          beat: 2.25,\n          id: '2cbb4cf7-d2c5-4b73-bd09-12476967bc32',\n        },\n        {\n          beat: 2.5,\n          id: 'b4a6edab-10c5-4b48-927f-fe36d8711e4b',\n        },\n        {\n          beat: 2.75,\n          id: 'b48dcdb6-085b-43f6-a5f5-430d5f357425',\n        },\n        {\n          beat: 3,\n          id: 'e69253b1-bedc-4126-843c-e8373732b347',\n        },\n        {\n          beat: 3.25,\n          id: 'c6d5f82a-699b-4b78-a37d-053d4d3dcb5b',\n        },\n        {\n          beat: 3.75,\n          id: 'b171d982-949f-41c7-96e5-34461596e5ab',\n        },\n        {\n          beat: 4,\n          id: '193c8efc-41e8-415f-9eb9-e3130cd5705f',\n        },\n        {\n          beat: 4.25,\n          id: '9300786a-def9-4a5c-afbb-576342fb80f5',\n        },\n        {\n          beat: 4.75,\n          id: 'cf3053bf-0e9c-4d2c-ac08-b47ee626b288',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'ef337ebe-946a-47c1-aedd-450564a97014',\n        },\n        {\n          beat: 2.5,\n          id: '9bca0ede-1f8e-4e0d-8af8-c5999609c001',\n        },\n        {\n          beat: 3.5,\n          id: '3a5729e6-34f5-47e6-8d50-7519415ca13b',\n        },\n        {\n          beat: 4.5,\n          id: 'ab176a41-9776-497a-af60-07ec36b23606',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'e2dbd7b2-1f0e-4dc8-b815-10beb669ff27',\n        },\n        {\n          beat: 2.5,\n          id: 'ddeb3419-3116-43d7-a0f2-34e46e4ae886',\n        },\n        {\n          beat: 3.5,\n          id: '9820216b-00d6-4295-b0b9-ea809e81e782',\n        },\n        {\n          beat: 4.5,\n          id: 'f1b516d1-a2fb-43d2-b42e-1596d3191b0d',\n        },\n      ],\n    ],\n    'fb2edb2c-3d21-49fd-98d8-459b153fb083': [\n      [\n        {\n          beat: 4.5,\n          id: '77238ce7-2fc0-44c2-8af2-789edaeba92e',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 4.5,\n          id: '03b7e1b0-2fb8-4dca-b982-6f0c77ae6673',\n        },\n      ],\n      [\n        {\n          beat: 2.5,\n          id: '07eb2589-17e6-45d8-b18b-5b4d4a0aa8f5',\n        },\n      ],\n      [\n        {\n          beat: 3.5,\n          id: 'afba7c4b-7f29-4602-8852-602d138f6946',\n        },\n        {\n          beat: 4.5,\n          id: '26924a16-d4c6-4b7f-b257-aaeb155db511',\n        },\n      ],\n      [],\n      [],\n    ],\n    '27f949f4-f5f7-4846-abfb-1d0e544b5bff': [\n      [\n        {\n          beat: 2.75,\n          id: '0f04bb15-6c36-4c58-974d-6aa004382576',\n        },\n        {\n          beat: 3.5,\n          id: 'e1e4bb50-c57b-4260-89a2-b3726635cfd4',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: '24746c40-9fb0-483c-939c-3842b767b1d4',\n        },\n        {\n          beat: 4.75,\n          id: 'e7d0ff1f-fa09-431e-9a1a-fc6aa7cf0a3f',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: '3d7b1deb-47dc-4029-b89e-0d701691b963',\n        },\n        {\n          beat: 4.75,\n          id: 'afb4d24b-41e1-4388-a724-04d90a637991',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '08e0071c-c3a3-4a96-aa1d-8c770c3b4c27',\n        },\n        {\n          beat: 1.5,\n          id: 'dee01a92-967a-4af0-8a19-a2899b5044e0',\n        },\n      ],\n    ],\n    'd8d65425-e980-43a6-b3b3-1c14b55b8c94': [\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '29d70f6a-df60-47d3-bbb5-5031c29e4390',\n        },\n      ],\n      [],\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '03a3e0e5-9278-4d51-a41c-6ec04de5de0f',\n        },\n      ],\n    ],\n    'dbbfd24a-0f5d-409f-b4ed-ec330b47e2e5': [\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: 'b1ce6e18-9e22-405f-bb53-0b60a2c0c2c3',\n        },\n        {\n          beat: 3,\n          id: '64e011d1-93e0-46ac-b6e1-c51955edece1',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '34dc07d7-1698-41be-bb7b-973f38a6dc16',\n        },\n        {\n          beat: 1.25,\n          id: 'a5e62b92-f494-431d-a325-1b747b27186b',\n        },\n        {\n          beat: 2,\n          id: '62e8cae9-f00d-47db-b985-b6acf219ed54',\n        },\n        {\n          beat: 2.75,\n          id: '7cea74a4-dd0d-43bf-9db3-f5b44c0de7f6',\n        },\n        {\n          beat: 2.5,\n          id: '5dd16303-e248-4b87-b988-ade5d68a0160',\n        },\n        {\n          beat: 3.5,\n          id: '20e36003-3b5d-43cb-a966-53d94bfe3379',\n        },\n        {\n          beat: 4.5,\n          id: '81d5797e-4c59-4a1d-ad4b-a77b916c757d',\n        },\n        {\n          beat: 4,\n          id: '53644024-31c7-4c18-a116-e2bca5e7fa36',\n        },\n      ],\n      [],\n      [],\n      [],\n      [],\n    ],\n    '206fcf08-67ff-4e98-905e-2495383fb83c': [\n      [],\n      [\n        {\n          beat: 3.5,\n          id: '34424649-30bb-4195-bc4c-46e5cb2dbd5c',\n        },\n        {\n          beat: 3.75,\n          id: '957478f5-b2b5-4feb-8626-0b41121764f3',\n        },\n        {\n          beat: 4.25,\n          id: 'b5e9576d-7cbf-4c8d-86bf-06e0eb66d685',\n        },\n        {\n          beat: 4.5,\n          id: '9946c9a4-c59e-4b32-8574-f65536dc56a6',\n        },\n        {\n          beat: 4,\n          id: 'd9b5d411-bb73-46f4-91f2-befc027c3b99',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: 'b9206d8e-272e-4e76-beee-ba008688cc67',\n        },\n        {\n          beat: 2.75,\n          id: 'd0e18285-2b00-4893-b8be-abad2a3f848b',\n        },\n        {\n          beat: 3.5,\n          id: '1f20cfc1-c299-416c-b141-4b42c05e16e9',\n        },\n        {\n          beat: 4.5,\n          id: 'f126fead-b0b9-4aca-9f88-2ca16d46ab12',\n        },\n        {\n          beat: 1.5,\n          id: '523e145e-ea70-47c6-b977-4eced0d34b12',\n        },\n        {\n          beat: 2.25,\n          id: '43d1e8de-2c07-447f-9e9e-d4df8ec42c68',\n        },\n      ],\n      [],\n      [],\n      [],\n    ],\n    '0ea2f943-2144-4434-92f7-8d689d49633a': [\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 1.75,\n          id: '69330ba3-a1d0-48a3-ba1e-f797c0571231',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'e280cdfd-291a-4562-90a7-287613e891b5',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1.75,\n          id: '10e9f6c6-698c-4d01-a9ea-508a213c435a',\n        },\n        {\n          beat: 3.75,\n          id: '97db3af1-342f-438c-a4ed-a45130b155ea',\n        },\n      ],\n      [],\n    ],\n    '2f090e95-37f7-4c49-b0c5-eb35d64216c8': [\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 2.5,\n          id: '37346254-891c-45a9-8a55-8cf653e79e97',\n        },\n      ],\n      [],\n      [],\n      [],\n      [],\n    ],\n    '816f7250-0afb-4353-bc6c-84ad97f9b8c6': [\n      [],\n      [\n        {\n          beat: 2.5,\n          id: '1af0deed-c640-4d2c-8b31-8675eb13b914',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '69ec2523-4eba-4ba0-83e2-35edb3ebc1a7',\n        },\n        {\n          beat: 3,\n          id: '3069e262-7b65-4da6-a401-eea332498f43',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 2.5,\n          id: '65bd6d4d-4b5e-45cd-ac0f-3ce821888d51',\n        },\n      ],\n      [],\n    ],\n  },\n};\n"
  },
  {
    "path": "src/presets/__mocks__/index.js",
    "content": "export default [\n  {\n    name: 'Roland 707',\n    bpm: 130,\n    channels: [\n      {\n        id: 'channel-id',\n        sample: 'channel-sample',\n        gain: 1,\n      },\n    ],\n    notes: [],\n  },\n  {\n    name: 'Roland 707',\n    bpm: 130,\n    channels: [\n      {\n        id: 'channel-id',\n        sample: 'channel-sample',\n        gain: 1,\n      },\n    ],\n    notes: [],\n  },\n];\n"
  },
  {
    "path": "src/presets/ace.js",
    "content": "import { samples } from '../samples.config';\n\nexport default {\n  name: 'Ace Drum',\n  bpm: 140,\n  swing: 0.3,\n  channels: [\n    {\n      id: 'empty_channel',\n      sample: samples.AcetoneBd,\n      gain: 1,\n    },\n    {\n      id: 'efd00923-e1b5-4bcd-b99c-2cbde8d92634',\n      sample: samples.AcetoneSd1,\n      gain: 0.73,\n    },\n    {\n      id: '6bf16a32-f8a1-4910-9046-be8b8c81591e',\n      sample: samples.AcetoneCh,\n      gain: 0.28,\n    },\n    {\n      id: 'ab6ea4cd-f211-43a3-95ae-9a1cf7459de2',\n      sample: samples.AcetoneOh,\n      gain: 0.46,\n    },\n    {\n      id: '8d761f84-9f82-48b3-bfa1-a11c7a7deeff',\n      sample: samples.AcetonePerc1,\n      gain: 0.45,\n    },\n    {\n      id: '8735d786-5ac0-4b36-a3fb-c58a55ee2b08',\n      sample: samples.AcetonePerc2,\n      gain: 0.41,\n    },\n  ],\n  notes: {\n    empty_channel: [\n      [\n        {\n          beat: 1,\n          id: '1d197b88-bc89-4047-abfa-4b9ff0027813',\n        },\n        {\n          beat: 3.5,\n          id: '207feb2f-a19d-452c-9d2e-928dc1eb587c',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '3dc0bd8b-be9c-4d03-85ad-d0efcbe9a375',\n        },\n        {\n          beat: 2.5,\n          id: '833ad698-fadb-4358-baba-cb6623bad000',\n        },\n        {\n          beat: 3.5,\n          id: '1a250f9b-9fc6-4cb0-b828-c287ada6b46b',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '93ed5e15-da3d-4904-9687-c4dc329dc8d8',\n        },\n        {\n          beat: 2.5,\n          id: '927ea811-5546-40a3-9b16-e936cf817b83',\n        },\n        {\n          beat: 3.5,\n          id: '735ec419-eb7a-4bad-979e-6f1109b3cb3d',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '632cc651-1a98-4594-bb7e-266cee5ddcde',\n        },\n        {\n          beat: 2.75,\n          id: '888ec786-218b-4318-bccb-df803e449ef8',\n        },\n        {\n          beat: 3.5,\n          id: 'fbe1f3ce-e6af-465c-8759-84161d83083f',\n        },\n        {\n          beat: 4.25,\n          id: '006f1ae9-87ee-49f0-aeae-54c1bf3f84ba',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'c949dc68-7b9c-4393-80f1-2879922fbf49',\n        },\n        {\n          beat: 2,\n          id: 'c7e21ef3-aea4-4275-96d3-6492cfbb1ca9',\n        },\n        {\n          beat: 3,\n          id: '1f8944c1-fecd-40c0-98f8-295b59d2403b',\n        },\n        {\n          beat: 3.75,\n          id: '47970992-a6d2-47b6-896d-c2a89e354986',\n        },\n        {\n          beat: 4.5,\n          id: '50a594a6-3de0-49a0-b3ce-b84135253de9',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'c0cb875b-302f-4072-a793-e870d79f4643',\n        },\n        {\n          beat: 4.5,\n          id: 'f04b647e-24c3-4a22-8464-704cd7e83dae',\n        },\n        {\n          beat: 3,\n          id: 'f6bd950c-2531-4b08-afbd-58241e01ccc7',\n        },\n        {\n          beat: 4,\n          id: '72d5107c-cc47-4c6e-9043-ae85b73783b9',\n        },\n        {\n          beat: 2,\n          id: '47174fff-ab43-46cb-b514-51de2aa3a07f',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '256a5c83-dd5a-4b9a-aad8-073280d52c52',\n        },\n        {\n          beat: 1.5,\n          id: '0305f16b-acb3-43a3-9ba3-6c5d4d0098bd',\n        },\n        {\n          beat: 2,\n          id: '48edd551-f99f-4c87-be19-a151e9c05716',\n        },\n        {\n          beat: 3,\n          id: '2757cc34-a21d-4149-ac91-aee19447f5fb',\n        },\n        {\n          beat: 3.25,\n          id: '65b5aa1e-4c06-41a4-9665-3c12e984fe6d',\n        },\n        {\n          beat: 4,\n          id: 'c4af9b8b-785f-4eee-8a9d-0231432c66f3',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '7ecb09d0-daf0-4e2b-82ce-d55f30440fda',\n        },\n        {\n          beat: 2.5,\n          id: '98f32a80-e900-4077-8ce7-ff693f1fdb1b',\n        },\n        {\n          beat: 2.75,\n          id: '9aad9f46-ed88-46ff-8660-d9c168507652',\n        },\n        {\n          beat: 3.5,\n          id: '69118664-0826-49a9-bbb4-c0325a73d771',\n        },\n        {\n          beat: 4.25,\n          id: '953259db-7370-4ecc-b503-2e13ab00ba50',\n        },\n      ],\n    ],\n    'efd00923-e1b5-4bcd-b99c-2cbde8d92634': [\n      [\n        {\n          beat: 2,\n          id: 'fa741890-aa03-43b8-8f45-e4f24dfd7c1d',\n        },\n        {\n          beat: 4,\n          id: '5df81234-d6ce-4113-936a-15303ab7bea9',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'fd4be3e8-e5cc-4249-8260-731c19e21f4b',\n        },\n        {\n          beat: 4,\n          id: '45af7433-99b8-415a-9688-40e2cc1c012e',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '50c05847-546e-4413-8835-95071e2c560e',\n        },\n        {\n          beat: 2,\n          id: '2539ca56-5ff2-41b0-b6c1-343e10cbdc3e',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '4ccc091a-f4db-483d-b117-005f388b19e8',\n        },\n        {\n          beat: 4,\n          id: 'c37ca0cd-84bb-44df-a4e9-395efc0c7603',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '3929c7e3-f56f-42bc-80f6-92fe885bdcd2',\n        },\n        {\n          beat: 4,\n          id: '94ddf04c-3096-45f4-a12f-ce10de25c0fe',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'b56918b1-9558-4679-884f-9c0d377a6777',\n        },\n        {\n          beat: 2,\n          id: 'c0c78af4-b022-4208-9c97-75938d4d89b6',\n        },\n        {\n          beat: 3,\n          id: 'c25f15bc-fcd7-4787-a9ea-58f5c03439e4',\n        },\n        {\n          beat: 3.75,\n          id: 'e7769870-3c75-473b-981b-6f444a2dcf78',\n        },\n        {\n          beat: 4.5,\n          id: '4aabf733-ec94-4755-9778-2a0cc522299d',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'a68fdced-bd7a-4cd5-a283-787099881710',\n        },\n        {\n          beat: 4,\n          id: 'ef791fa3-4566-4fe4-8d5f-13e0863f3744',\n        },\n        {\n          beat: 4.5,\n          id: '4ae59c1f-973b-4607-af96-179258cd7a76',\n        },\n        {\n          beat: 4.75,\n          id: '11228b04-49ab-4845-b60a-1691299701e3',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'efaab763-2dbe-45e2-9a31-dbf793d83879',\n        },\n        {\n          beat: 4,\n          id: '37a3521d-02e1-4ef7-827d-b2d9d243e140',\n        },\n      ],\n    ],\n    '6bf16a32-f8a1-4910-9046-be8b8c81591e': [\n      [\n        {\n          beat: 1,\n          id: '78f93101-1efe-4081-a917-fdbe5b9adf92',\n        },\n        {\n          beat: 2,\n          id: '1643b6bb-9242-4caf-bf82-2286f5457314',\n        },\n        {\n          beat: 3,\n          id: '34bebd08-a9d9-4325-9ffa-8e532587d156',\n        },\n        {\n          beat: 4,\n          id: 'fc8fd45c-7834-41f6-afd7-88429dc56a90',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '2f75c27d-2ded-4176-8d71-de32d8901c39',\n        },\n        {\n          beat: 1.5,\n          id: 'a4d10956-006f-4658-92ff-5fd653f61c1a',\n        },\n        {\n          beat: 2,\n          id: 'ce068960-2854-4784-9a5e-5d9c19d22e5a',\n        },\n        {\n          beat: 2.5,\n          id: '50b400c6-7913-4391-bb8b-dddec30e94a4',\n        },\n        {\n          beat: 3,\n          id: '0f235fc4-ba58-475f-b450-b41bb639fa17',\n        },\n        {\n          beat: 3.5,\n          id: 'a961c8c3-5ade-4f66-afe8-a6c356c6e0da',\n        },\n        {\n          beat: 4,\n          id: 'bf747af4-2102-407e-8f8b-b5b42a2de454',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '5a277a9e-8068-4756-a455-123ee20d0296',\n        },\n        {\n          beat: 3,\n          id: '88c03234-5353-4f2e-b9ff-3c6fa9c05c5a',\n        },\n        {\n          beat: 4.5,\n          id: '6e94f0b3-e560-4367-b77b-9b4dccdba2e3',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '925e99ee-c598-4081-bcce-d8899261dd19',\n        },\n        {\n          beat: 2,\n          id: '44d4f22b-52dc-4fc9-ba64-745e498c04d9',\n        },\n        {\n          beat: 2.5,\n          id: '39d569a0-c87b-4faa-a9eb-2ac4a5219bae',\n        },\n        {\n          beat: 3,\n          id: '2040ccbf-9693-47f9-a2ad-2524595152df',\n        },\n        {\n          beat: 3.5,\n          id: 'a79f9f2e-7585-4234-be3d-41f143448d9e',\n        },\n        {\n          beat: 4,\n          id: '444ff59e-a333-4a8f-aef2-80b9c28b21d1',\n        },\n        {\n          beat: 4.25,\n          id: '3fc91e38-866a-442c-8af9-aed48c9fd15f',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '50d19196-01b9-4260-bec1-9a38e6b2e3ee',\n        },\n        {\n          beat: 2.5,\n          id: '85d11a31-3723-403e-baf7-ca7e4bc68e7a',\n        },\n        {\n          beat: 3.5,\n          id: '83ff99bb-affb-4076-8f5c-4e09b587815b',\n        },\n        {\n          beat: 4.5,\n          id: '734bc331-735e-42fd-bf17-9b75f53472ce',\n        },\n      ],\n      [\n        {\n          beat: 3.5,\n          id: '96c79893-8b36-4260-865d-62ef35bd0204',\n        },\n        {\n          beat: 4.5,\n          id: 'b3da5898-9761-4563-b5a0-0af1c399d72f',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'acc5c4b0-cb58-4430-b9a6-45148b35d4ec',\n        },\n        {\n          beat: 1.5,\n          id: 'de3559e6-e087-4ea2-ad01-8ca24a66e1da',\n        },\n        {\n          beat: 2,\n          id: '4e0f84df-c163-4666-9601-c63d9a782b62',\n        },\n        {\n          beat: 2.5,\n          id: '748cae03-1a09-44f3-8fd2-c7c04a170885',\n        },\n        {\n          beat: 3,\n          id: 'dbe92a4f-69fc-4525-99bf-a02047745bb6',\n        },\n        {\n          beat: 3.5,\n          id: 'a19739e4-0850-47be-8515-0aa1d3dddb9e',\n        },\n        {\n          beat: 4,\n          id: '7c7b50cd-49c7-4691-8b97-e6db431265f3',\n        },\n        {\n          beat: 4.25,\n          id: '1d44e23b-d1c3-42db-b5ea-06a32aac407d',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '17f3f971-06b9-4eae-97ba-4018affbd3a8',\n        },\n        {\n          beat: 4.5,\n          id: '0053433c-dd30-46d1-87b5-6718ad1eaf52',\n        },\n        {\n          beat: 1,\n          id: 'a89b9d91-d035-49f1-9b50-b4a65f0139d4',\n        },\n        {\n          beat: 1.5,\n          id: 'ec68efa2-a52c-4622-b63b-88f19e2d55ac',\n        },\n        {\n          beat: 3,\n          id: 'c28629f1-77cf-4523-9f72-52e4b789a394',\n        },\n        {\n          beat: 4,\n          id: 'aab2f193-bfe4-4541-98e6-dbd3197b0ffc',\n        },\n      ],\n    ],\n    'ab6ea4cd-f211-43a3-95ae-9a1cf7459de2': [\n      [\n        {\n          beat: 4.5,\n          id: '07fa6ab6-750e-4391-995d-31f7f5827c5c',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: '3bb0e308-be8b-4c09-923c-80b168181efe',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '813addc6-8d78-4c37-8498-1c04723528a0',\n        },\n        {\n          beat: 4,\n          id: '7854fc2e-b394-4ca7-92cd-b4bf7171d299',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'e538d894-6c24-45f2-adfb-b0e10cf21fd8',\n        },\n        {\n          beat: 4.5,\n          id: '76221cb4-1e93-479e-82ce-cee6cb7b8eb6',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: '6bd88f44-43c0-42d6-8e03-89f029f254ae',\n        },\n        {\n          beat: 2.5,\n          id: '05411202-e052-416c-92d1-bdc4918bcbc9',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: '74cf1b66-000f-42e5-a56c-03ec29b778aa',\n        },\n      ],\n      [\n        {\n          beat: 3.5,\n          id: 'b97d36aa-f1c9-40b5-8d84-b90c8bf93507',\n        },\n        {\n          beat: 2.5,\n          id: 'd4b1e512-6334-4658-b617-dc32bb582dbd',\n        },\n      ],\n    ],\n    '8d761f84-9f82-48b3-bfa1-a11c7a7deeff': [\n      [\n        {\n          beat: 1,\n          id: 'd6726903-94b1-4cbb-857e-27c5e1a40431',\n        },\n        {\n          beat: 2.5,\n          id: '2c10b8fc-bce7-4561-9b21-dce14118d55d',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'b20fd85d-8aaa-4ca6-b3fa-9190e55aa0ea',\n        },\n        {\n          beat: 2.5,\n          id: 'd86c62d6-4ed3-4cd3-9987-b60382ec74d3',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '969aa4d1-010d-4107-8474-081841c33465',\n        },\n        {\n          beat: 1.75,\n          id: '78f75939-efd5-49cb-ac49-86fb9ca3b7a6',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: 'f6fe983f-36b9-4445-9962-d583a50f6ded',\n        },\n        {\n          beat: 1.5,\n          id: 'f5ad27ce-13a8-45af-b6b6-5944c645fc59',\n        },\n        {\n          beat: 2,\n          id: '5a7e221d-467d-4dda-a12a-000ff9b9ca15',\n        },\n        {\n          beat: 2.5,\n          id: '14ff4fcc-742a-4745-989b-4bfa1e1d5b89',\n        },\n        {\n          beat: 3,\n          id: 'd6d8889a-3f5e-443c-be69-ea212752fb51',\n        },\n        {\n          beat: 3.75,\n          id: '20f95ff5-77ef-4736-aa3a-2887f16f48a0',\n        },\n        {\n          beat: 4.5,\n          id: '53f31cb5-81c5-49ea-bbc6-9aaa4c7d4619',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'fdcfa8fa-90da-4c43-a515-92030094a7d7',\n        },\n        {\n          beat: 3,\n          id: '06b59b4d-ddc1-4217-848d-8ea2babcf16a',\n        },\n        {\n          beat: 3.75,\n          id: 'c1d478bf-8837-464d-8b37-26314f332fd5',\n        },\n        {\n          beat: 4.5,\n          id: 'a315391d-9da3-42d2-877c-757c3e4f641c',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 4.5,\n          id: '835e969b-f3d2-4be8-bed7-e5aa641045f3',\n        },\n      ],\n    ],\n    '8735d786-5ac0-4b36-a3fb-c58a55ee2b08': [\n      [],\n      [\n        {\n          beat: 3.5,\n          id: '5f8ee195-3f73-4c07-badf-4a365aef6dfd',\n        },\n      ],\n      [\n        {\n          beat: 2.75,\n          id: '39aa3d74-a386-4cd6-832c-89a2c741e5d6',\n        },\n        {\n          beat: 3.5,\n          id: '7dbeb24e-7995-47cc-a84f-cb762fdfbb28',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'b636a41e-a9d6-444f-9b6e-3d86c0a34c79',\n        },\n        {\n          beat: 3,\n          id: '2b365537-7fc5-48b4-8e80-988d9991eed7',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '15624899-9287-47fb-8bdc-96a979bcf8e2',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 4.75,\n          id: '578a74ab-f44e-49c4-9a00-625e87181fe5',\n        },\n      ],\n    ],\n  },\n};\n"
  },
  {
    "path": "src/presets/empty.js",
    "content": "import sampleList from '../samples.config';\n\nexport const EMPTY_NOTE_ROW = [[], [], [], [], [], [], [], []];\n\nexport default {\n  name: 'Empty',\n  bpm: 80,\n  swing: 0,\n  channels: [\n    {\n      id: 'empty_channel',\n      sample: sampleList[0].url,\n      gain: 1,\n    },\n  ],\n  notes: {\n    empty_channel: EMPTY_NOTE_ROW,\n  },\n};\n"
  },
  {
    "path": "src/presets/hip-hop.js",
    "content": "import { samples } from '../samples.config';\n\nexport default {\n  name: 'Hip Hop',\n  bpm: 98,\n  swing: 0.4,\n  channels: [\n    {\n      id: 'e75658e5-e17b-47dd-b8ca-4d03b06068d9',\n      sample: samples.HipHopBd1,\n      gain: 1,\n    },\n    {\n      id: 'empty_channel',\n      sample: samples.HipHopBd2,\n      gain: 1,\n    },\n    {\n      id: '8c754c56-6cdc-479a-95b2-890ce93856c2',\n      sample: samples.HipHopSd1,\n      gain: 1,\n    },\n    {\n      id: '6182dd6e-188c-4157-9b97-86d904f446d5',\n      sample: samples.HipHopSd2,\n      gain: 0.6,\n    },\n    {\n      id: '5c34f5d0-6605-471b-95f1-eea186f332ea',\n      sample: samples.HipHopCh1,\n      gain: 1,\n    },\n    {\n      id: '3a8d1712-d85b-4a1c-b812-4e16da1f3731',\n      sample: samples.HipHopCh2,\n      gain: 0.46,\n    },\n    {\n      id: '26feb931-cfb9-4a93-a0ab-6f9ca189bb40',\n      sample: samples.HipHopOh,\n      gain: 0.33,\n    },\n  ],\n  notes: {\n    empty_channel: [\n      [\n        {\n          beat: 1,\n          id: 'd3c6c0be-f5bb-4ac7-bc0f-b491ad6d430d',\n        },\n        {\n          beat: 3,\n          id: 'd1e824b8-2d4c-4c73-8d46-14761d7bd50d',\n        },\n        {\n          beat: 3.5,\n          id: 'cbc9b4de-5b86-40a9-98d9-5bbce91496dd',\n        },\n        {\n          beat: 4.5,\n          id: '3dd7953e-9d07-471c-9dfd-fd0d5f55ead4',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'e1e6024c-4ef0-409e-961c-d1f885587b3e',\n        },\n        {\n          beat: 1.75,\n          id: 'ff1ff67c-c19c-4a17-a28d-69b707b5b013',\n        },\n        {\n          beat: 2,\n          id: 'c559f161-50ef-41e0-88e2-9d6487f84f00',\n        },\n        {\n          beat: 2.75,\n          id: '8ce49ba6-e328-4cb6-be6d-e04e6328603c',\n        },\n        {\n          beat: 3,\n          id: 'ae33399b-725e-4fb3-a5b5-dcb88e62b611',\n        },\n        {\n          beat: 3.75,\n          id: '99175bf1-c886-4064-a4b1-52a0713b9e9f',\n        },\n        {\n          beat: 4,\n          id: 'd481c0d1-501f-48ef-8d2a-e3975907e609',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '6ac66ee0-20c9-4ce2-a1fc-6eb8563fcbad',\n        },\n        {\n          beat: 2.75,\n          id: '18b6a0cb-fa7d-42c2-89f2-dd341649dddb',\n        },\n        {\n          beat: 3.5,\n          id: '3e09868f-745e-4d46-b151-1ed985e6ea1d',\n        },\n      ],\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: 'de69c191-f2ed-4990-b838-c8b08e28d2f7',\n        },\n        {\n          beat: 2.5,\n          id: 'eb13514a-180e-473c-9b91-64031760640e',\n        },\n        {\n          beat: 3.5,\n          id: 'bebb2046-f859-4c1e-befc-d8159c746dd5',\n        },\n      ],\n      [],\n    ],\n    '8c754c56-6cdc-479a-95b2-890ce93856c2': [\n      [\n        {\n          beat: 2,\n          id: 'a6df6bb6-cbe8-4115-bb97-0c6a201cbc67',\n        },\n        {\n          beat: 4,\n          id: 'beb74b51-6bac-461d-8f9d-6197c9909364',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '6697284c-72e5-44bd-90c8-be9c064fc65f',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: 'c2ecc059-975f-4d9a-88d6-fab3eac53224',\n        },\n        {\n          beat: 2,\n          id: 'df1c1f60-ce06-4457-8895-335d4996c0f3',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '04bec360-6f92-453d-aea7-61befc0731c1',\n        },\n        {\n          beat: 4,\n          id: '3389f856-266e-4f1b-9953-67ced6637cee',\n        },\n      ],\n      [\n        {\n          beat: 2.75,\n          id: 'a1ed3fef-94d0-401b-9365-eee79596ed80',\n        },\n        {\n          beat: 2,\n          id: 'b4e4c591-ba8d-4d85-af7c-6429a8096290',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '4d0f189e-44f1-4f04-9de0-25f637684e35',\n        },\n        {\n          beat: 2,\n          id: '9ea7858b-b2a4-425d-ad01-8f73d64f2480',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'f7034f89-4832-4fef-8e74-af464fb28c60',\n        },\n        {\n          beat: 4,\n          id: 'dc1fe315-ba48-49a5-861c-e305a38181db',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '887ba194-4ee1-4b59-9c92-698170137d83',\n        },\n        {\n          beat: 2,\n          id: '8307c06e-5df6-4209-87b0-020b9b5dd268',\n        },\n        {\n          beat: 2.75,\n          id: 'a3325cf0-1e16-4b27-a72a-8ba8461a4ed7',\n        },\n        {\n          beat: 4,\n          id: '3eb6e9c4-9b24-4f24-82a4-20bd6fff4f86',\n        },\n        {\n          beat: 4.25,\n          id: '16079935-eab8-4083-8d97-ad29dd8137c8',\n        },\n        {\n          beat: 4.5,\n          id: '8818fa69-74e0-468d-8ce2-5ce3c33640ae',\n        },\n        {\n          beat: 4.75,\n          id: '31a5d2bf-9d81-4a80-8e79-3a31e7cd4bde',\n        },\n      ],\n    ],\n    '5c34f5d0-6605-471b-95f1-eea186f332ea': [\n      [\n        {\n          beat: 1.5,\n          id: '7cfe904a-1a84-44b4-b7e6-b3f9556045a6',\n        },\n        {\n          beat: 2.5,\n          id: '549656ec-7445-46c0-9e3b-034ddeaf74bb',\n        },\n        {\n          beat: 3.5,\n          id: '53c9e7e8-d207-42ea-b42f-a25c7c2b10fe',\n        },\n        {\n          beat: 4.5,\n          id: '0aabcd2e-6bee-4212-afbe-6e5035aab297',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '340e9484-71f7-4e95-88d7-93f4068c15ea',\n        },\n        {\n          beat: 2.5,\n          id: 'a1726663-0aad-4983-9d94-a80c725edcdb',\n        },\n        {\n          beat: 3.5,\n          id: 'e6bfb5f0-27d0-498e-9d17-77ace30629a3',\n        },\n        {\n          beat: 4.5,\n          id: '201e2706-784e-4194-9449-d6c7476e1697',\n        },\n      ],\n      [\n        {\n          beat: 3.5,\n          id: 'dff397b4-b6eb-4f9e-8546-9fed2b091023',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '4342fcc5-40be-4ba9-98ac-867b2bf96dba',\n        },\n        {\n          beat: 4.5,\n          id: '24433b21-42f6-428d-832a-8e87b026bc52',\n        },\n        {\n          beat: 3.25,\n          id: '17511f0c-f907-4a21-845d-0f0ab587f4af',\n        },\n        {\n          beat: 1.5,\n          id: '4eca3412-5097-408e-9ad8-0fe1e8184a71',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'f2a1d585-318c-43ce-be75-e40062bd11ec',\n        },\n        {\n          beat: 2.5,\n          id: 'f09b258c-7609-4cd6-8479-1f040f7b29f7',\n        },\n        {\n          beat: 3.5,\n          id: 'fbdbd55b-5912-4391-b5e2-fa6d007a73a1',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '3bdda5b4-acc2-4a34-a939-7c5c382aa349',\n        },\n        {\n          beat: 2.5,\n          id: '4592ea4f-7db4-403e-ba95-b565fc14c70e',\n        },\n      ],\n      [\n        {\n          beat: 1.25,\n          id: '9947817d-817f-42b8-a307-7342db701165',\n        },\n        {\n          beat: 2,\n          id: 'c0e01823-710b-4164-b319-74210ad518e8',\n        },\n        {\n          beat: 2.25,\n          id: '84f70ba1-87a3-4c8c-aca6-ac499cf7e2ce',\n        },\n        {\n          beat: 2.75,\n          id: '161ed9ce-767f-4f59-80f5-18a1beaa4c9c',\n        },\n        {\n          beat: 3.25,\n          id: '42871ece-729b-4d35-b067-24d571e01149',\n        },\n        {\n          beat: 3.75,\n          id: 'ec13afb1-ae07-483d-92d8-f62d7e8af142',\n        },\n        {\n          beat: 4.25,\n          id: '5824151b-8e50-46c3-b4b1-c2c5fa4bd71a',\n        },\n        {\n          beat: 4.75,\n          id: '4697821e-184c-4ee5-8273-53c177bcbe47',\n        },\n        {\n          beat: 1.75,\n          id: '88dda45f-6912-41ac-a0f0-0190f87d35a0',\n        },\n        {\n          beat: 1.5,\n          id: 'c8ab9783-59f1-4b41-b3e4-1d0cf0f2b666',\n        },\n        {\n          beat: 3,\n          id: '627da411-a872-45d2-b30e-d6c19add874c',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '37ff0f84-0c41-4d12-a013-35b841332186',\n        },\n        {\n          beat: 3.75,\n          id: '33065041-592e-4631-bc66-67a89cdc6504',\n        },\n      ],\n    ],\n    '3a8d1712-d85b-4a1c-b812-4e16da1f3731': [\n      [\n        {\n          beat: 1,\n          id: '595f6d29-46da-49f5-a05d-0e7411e4ec89',\n        },\n        {\n          beat: 3,\n          id: 'db6aa05f-e63a-4205-a05f-00d816b00f58',\n        },\n      ],\n      [\n        {\n          beat: 3,\n          id: 'ec5c2059-0ee4-4a3d-b987-e210d96e2e7b',\n        },\n        {\n          beat: 1,\n          id: '4f625cda-20c7-47c7-8bef-dcf76f49af1d',\n        },\n        {\n          beat: 2,\n          id: '868dd5cd-e763-4d4a-aed0-27976a8f2485',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '7ed9a16e-e674-49a8-a2fe-fb60a18782f6',\n        },\n        {\n          beat: 2,\n          id: '1107910d-48e4-4bb8-b773-884f863e387e',\n        },\n        {\n          beat: 4,\n          id: 'a3b4052f-4012-4bd0-b516-948319144448',\n        },\n        {\n          beat: 3,\n          id: 'eff4244b-8533-4807-bef2-586ab5b5c48a',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '6044a1a4-83db-4827-8b14-9bff985533e2',\n        },\n        {\n          beat: 4,\n          id: '79dec85a-c49a-4687-bc6b-139f22eb457b',\n        },\n        {\n          beat: 2.75,\n          id: 'cdc4e782-6628-4415-9eae-3eb20a076e0e',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '55c8b1e8-0d3f-440a-8918-72ebfb68ad62',\n        },\n        {\n          beat: 2,\n          id: 'f395b792-a4b0-4b96-9bb1-c4f082ee055c',\n        },\n        {\n          beat: 3,\n          id: '7c4a3ec0-50d9-4ae7-b7c0-363a2fd5815f',\n        },\n        {\n          beat: 4,\n          id: 'c3f87d26-0e84-44e5-b36c-697d3a8a608a',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'e76e6472-75ba-4de6-a6ba-8fd61dab9bff',\n        },\n        {\n          beat: 3,\n          id: '3f8c7947-0d92-4cee-9e82-ea9fa16eb4b9',\n        },\n        {\n          beat: 3.5,\n          id: '469785b0-9f13-4ce3-ab59-f4569a84d585',\n        },\n        {\n          beat: 4,\n          id: 'ac2022f7-dd6b-47ed-9425-d474d2337785',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'd0db5bf2-72cd-47c8-96c9-9738c92b98de',\n        },\n        {\n          beat: 2.5,\n          id: 'a45af499-562a-46cf-9094-11b6a4fec17c',\n        },\n        {\n          beat: 4,\n          id: '9d3447fb-26c5-4fb9-bd9f-c2a13cd1d91a',\n        },\n        {\n          beat: 3.5,\n          id: '8030f3d7-9e0d-4e1e-9ad9-32a86d30f0a1',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'c3ce07ba-e4c1-4ad9-82b6-b51761c3826d',\n        },\n        {\n          beat: 4.25,\n          id: 'b4810e34-e93e-4f80-9a94-833865e46dbc',\n        },\n        {\n          beat: 4.75,\n          id: 'bcd3e697-9028-4c8b-a3b7-48e9d4f435d4',\n        },\n        {\n          beat: 2,\n          id: 'a7292ad7-2557-435f-ae47-be9028cd18a9',\n        },\n        {\n          beat: 3.25,\n          id: '74ff49fc-b78a-4406-9519-ab21d21ec350',\n        },\n      ],\n    ],\n    '6182dd6e-188c-4157-9b97-86d904f446d5': [\n      [\n        {\n          beat: 4,\n          id: '0e7f5542-7eee-47eb-8581-702434ac7f24',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'b5d20e2a-a7ff-4247-a97e-8009d3068f52',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '1285f3cf-2e23-4810-9076-0cb0cd34fb9f',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '8f070f1a-ab0d-4cfc-9240-478ef9b209eb',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'f8364458-f508-4c9c-aab3-7c57c99328f9',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: 'c243b510-f46d-4ba7-963a-f5718c56dd45',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '8538a736-527c-4d88-a579-6cff14cb445e',\n        },\n        {\n          beat: 4.75,\n          id: '3aa58d8b-03b6-400d-b919-6c83af261832',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '8f29d886-c7b6-492d-ac0b-1d122e8a7b04',\n        },\n        {\n          beat: 4,\n          id: 'f2f2acb4-a893-45e0-9a7b-e6b05739d784',\n        },\n        {\n          beat: 2.75,\n          id: '42087e3b-05ea-4475-b3e4-fa9d78997b87',\n        },\n        {\n          beat: 4.75,\n          id: '9522b33d-5c5a-49ed-8472-6622af4b25b9',\n        },\n      ],\n    ],\n    '26feb931-cfb9-4a93-a0ab-6f9ca189bb40': [\n      [\n        {\n          beat: 4.5,\n          id: '169b577c-59ab-47b3-b1b2-7902029ecc81',\n        },\n      ],\n      [\n        {\n          beat: 4,\n          id: '3f51236d-7d20-45a1-9733-b4870fb6eeb8',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'd226b632-eada-405a-b9a6-afdfb7bf178e',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'd8074acc-5baf-4e70-bb25-f1fdb846316b',\n        },\n        {\n          beat: 3.5,\n          id: '4f1900c2-64d1-4912-83e1-d43693d02248',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: '582fbf75-84a2-4b65-81c1-8982df340d33',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '87055d0b-3d04-498f-b7c7-60bdad4bb420',\n        },\n        {\n          beat: 4.5,\n          id: '4ece5f5a-68a2-4b20-9b4f-dc09565bdfba',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'c5b96f26-e6ba-4cf0-a909-f8de9a429046',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '16e46966-326c-4690-a71b-835c1bae05e7',\n        },\n        {\n          beat: 2.75,\n          id: '03755834-8d4a-432d-9efd-29cf02eba211',\n        },\n        {\n          beat: 4,\n          id: '052e1671-ce04-4d3d-b075-7d1f3d65110e',\n        },\n        {\n          beat: 4.5,\n          id: '630eeb48-9718-468d-9a47-a4011264abed',\n        },\n      ],\n    ],\n    'e75658e5-e17b-47dd-b8ca-4d03b06068d9': [\n      [\n        {\n          beat: 2.75,\n          id: '686d2334-4323-4eec-a829-f8aa12adc25c',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 3.25,\n          id: '9e6fbd33-5aa2-44b1-a636-5ac99bcc145a',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1,\n          id: '842ecdc5-f92f-4b94-8233-c277bca2cc01',\n        },\n        {\n          beat: 3.5,\n          id: 'ec3a001f-fe77-4262-b3df-a1a61bdc095f',\n        },\n        {\n          beat: 4.5,\n          id: '674f9739-5c27-4f54-994c-a456400e9134',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'aa146f7d-3d84-4ec0-bfce-597f6c82459e',\n        },\n        {\n          beat: 2.75,\n          id: '3663c589-85de-4233-b9ff-22cf3c79ba3b',\n        },\n        {\n          beat: 3,\n          id: '593b5441-f935-4b95-badd-f1c91c7ea2de',\n        },\n        {\n          beat: 3.5,\n          id: 'ac1590d7-2b09-46f9-98f3-53f28cbf0e3c',\n        },\n        {\n          beat: 4.5,\n          id: '6897a6f9-6dd1-47bd-b1ac-3b99116fbebf',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2.5,\n          id: '22862c2b-56a7-4a65-a407-f5cf72414bf1',\n        },\n        {\n          beat: 1,\n          id: '54be7db8-3e8e-4097-be8a-90d8f3884604',\n        },\n        {\n          beat: 3.25,\n          id: '0d86e28e-fd88-4aa0-a636-53fd1a2468b2',\n        },\n        {\n          beat: 3.75,\n          id: 'dcc8d5ec-6a42-4647-849c-dd22f2259c0c',\n        },\n      ],\n    ],\n  },\n};\n"
  },
  {
    "path": "src/presets/index.js",
    "content": "import empty from './empty';\nimport hipHop from './hip-hop';\nimport lDrum from './ldrum';\nimport sevenohseven from './707';\nimport eightoheight from './808';\nimport ace from './ace';\n\nexport default [empty, eightoheight, ace, lDrum, hipHop, sevenohseven];\n"
  },
  {
    "path": "src/presets/ldrum.js",
    "content": "import { samples } from '../samples.config';\n\nexport default {\n  name: 'LDrum',\n  bpm: 124,\n  swing: 0.2,\n  channels: [\n    {\n      id: 'empty_channel',\n      sample: samples.LinnBd,\n      gain: 1,\n    },\n    {\n      id: 'eca3906c-9577-4a38-a025-87f6c7b8fa88',\n      sample: samples.LinnSd,\n      gain: 0.65,\n      reverb: 0.1,\n      pan: 0,\n    },\n    {\n      id: 'c068fa91-9977-4fb1-9f41-ec3fe8473cea',\n      sample: samples.LinnCh,\n      gain: 0.1,\n      reverb: 0.2,\n      pan: 0.3,\n    },\n    {\n      id: '4ed97fa7-8798-4cf6-8c7b-0e42c76f1612',\n      sample: samples.LinnPh,\n      gain: 0.15,\n      reverb: 0.2,\n      pan: 0.3,\n    },\n    {\n      id: '7ac094ef-282c-4d9d-8d7c-390118dd925a',\n      sample: samples.LinnHt,\n      gain: 0.26,\n      reverb: 0.35,\n      pan: -0.5,\n    },\n    {\n      id: '046c982a-2576-453d-b021-963c6d3076ee',\n      sample: samples.LinnMt,\n      gain: 0.27,\n      reverb: 0.35,\n      pan: -0.3,\n    },\n    {\n      id: '12a11640-05d9-4a1e-88ff-c4c7525385b1',\n      sample: samples.LinnLt,\n      gain: 0.3,\n      reverb: 0.35,\n      pan: -0.1,\n    },\n    {\n      id: 'e72fec64-9e3d-4848-81ee-c35643a70624',\n      sample: samples.LinnCowbell,\n      gain: 0.24,\n      reverb: 0.2,\n      pan: 0.4,\n    },\n    {\n      id: '06126c09-a20b-4cf4-8c05-a812d3ebc7c8',\n      sample: samples.LinnClap,\n      gain: 0.44,\n      reverb: 0.1,\n      pan: -0.5,\n    },\n    {\n      id: 'c4b37dfa-8f2f-491b-942a-b271b7b24c71',\n      sample: samples.LinnRim,\n      gain: 0.43,\n      reverb: 0.1,\n      pan: 0.5,\n    },\n    {\n      id: '3bd51432-6e07-4ec5-93b0-04eef8be49f6',\n      sample: samples.LinnTamb,\n      gain: 0.28,\n      reverb: 0.3,\n      pan: 0.7,\n    },\n  ],\n  notes: {\n    empty_channel: [\n      [\n        {\n          beat: 1,\n          id: 'f9537c53-f916-434d-9697-86e500da2414',\n        },\n        {\n          beat: 2,\n          id: 'a8aecd47-1ef3-433a-8844-ad0b6e9550ff',\n        },\n        {\n          beat: 3,\n          id: '94e06d7f-fa55-4314-9caf-27191b706b8c',\n        },\n        {\n          beat: 4,\n          id: '9a610633-0c4e-4ebc-a907-da90f1a98e9e',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '38af500c-c153-4f12-bc5c-2c8ec6b8eafa',\n        },\n        {\n          beat: 2,\n          id: '4eb43cec-9542-4a0e-9616-35fa43b45d56',\n        },\n        {\n          beat: 4,\n          id: '5b1c927e-6eb7-489e-bf67-9e59620467bb',\n        },\n        {\n          beat: 3,\n          id: 'de2cf269-ea81-45f3-9b68-ccbf4fe41ca9',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '1c6b732a-ce76-4f07-90f2-7ba9d3f14da0',\n        },\n        {\n          beat: 2,\n          id: 'e2793169-3989-4e5d-bb97-91c642172104',\n        },\n        {\n          beat: 3,\n          id: '28009164-8f89-4a17-82cb-2c904be196a8',\n        },\n        {\n          beat: 4,\n          id: 'c4285da8-5e44-44a7-8a09-f7d0da6b3aa2',\n        },\n        {\n          beat: 4.5,\n          id: 'f5768e8f-b408-4dcf-a86b-1a65eac0f13c',\n        },\n        {\n          beat: 4.75,\n          id: 'd2ddfcdf-2cf6-420b-ae35-56c49e4f8f4e',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '43069058-8f8e-4423-a9be-ce4f469b6f0f',\n        },\n        {\n          beat: 3.5,\n          id: '4c901c5a-7ace-42e8-89bc-d8381a6aca65',\n        },\n        {\n          beat: 4.25,\n          id: '9b0af2ff-3596-40f9-86f7-de123046b27a',\n        },\n        {\n          beat: 2.5,\n          id: '7e4505e6-446a-4855-9465-9f9e204a1de6',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '6a394bca-a802-46a2-9bda-f0376793de35',\n        },\n        {\n          beat: 2,\n          id: '1c7de135-b429-480f-8780-2f95722f4492',\n        },\n        {\n          beat: 3,\n          id: 'b6405cf3-6f51-4a09-8a50-2e1f9321d754',\n        },\n        {\n          beat: 4,\n          id: 'dc15b93a-affb-47bf-87a2-ddeb1b1c54b6',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '85dfaf1a-d43b-4f8f-b277-1edba8188568',\n        },\n        {\n          beat: 1.75,\n          id: '94bca68b-ea37-4ceb-b858-ea835e8bec5e',\n        },\n        {\n          beat: 2.5,\n          id: '57618e04-00b0-44cf-ae9b-1449fbf67cc0',\n        },\n        {\n          beat: 3.5,\n          id: 'b8cb485a-1a11-4054-ac6d-e95f78f0a327',\n        },\n        {\n          beat: 3.75,\n          id: 'e13a78a0-aed9-4db9-a902-bba81e678b46',\n        },\n        {\n          beat: 4.5,\n          id: '2b2e507c-d383-4a99-a8ac-b2ad0eb03afb',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'abf70cdc-f4ab-477b-8b14-5bdcce752a4b',\n        },\n        {\n          beat: 4.5,\n          id: '55a3ce00-1723-420b-b8df-1ae1dad93e2e',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '946594e8-8569-4ea5-8cbd-667faad73cd4',\n        },\n        {\n          beat: 2,\n          id: '5239b5f7-a009-40d1-8ca9-77f3f5e59c39',\n        },\n        {\n          beat: 3,\n          id: 'b67ca07c-9791-4f32-8134-1187e4bb5119',\n        },\n        {\n          beat: 3.25,\n          id: '600a8ac1-5206-4545-86e0-3df14f165dd2',\n        },\n        {\n          beat: 3.5,\n          id: 'b43191ed-36f0-4fad-a909-68c8736e72a5',\n        },\n        {\n          beat: 3.75,\n          id: '977fa833-0b6b-4e2c-a634-74d0082141b0',\n        },\n        {\n          beat: 4,\n          id: 'd63fcab7-ed08-42e0-a56f-f78e9265f0bc',\n        },\n        {\n          beat: 4.25,\n          id: 'f02eb38c-f046-4d7c-9792-38a32e258926',\n        },\n        {\n          beat: 4.5,\n          id: 'debc09eb-682b-4790-843d-c54f92e4801b',\n        },\n        {\n          beat: 4.75,\n          id: '98bbe8b5-bb5e-4585-89bf-a148d15fe14b',\n        },\n      ],\n    ],\n    'eca3906c-9577-4a38-a025-87f6c7b8fa88': [\n      [\n        {\n          beat: 2,\n          id: 'c363f89a-cc72-4d2f-aa9e-2bc566f9910e',\n        },\n        {\n          beat: 4,\n          id: '18f833c1-3fbe-49de-8556-0e564534f3f2',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'a384c923-751d-4033-a556-ebacb2469a1a',\n        },\n        {\n          beat: 4,\n          id: 'c24c4906-fbd2-48cd-a400-5e5bb15de3b2',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2,\n          id: '00f68589-77b4-47a9-9e1a-b3d9e44a12a3',\n        },\n        {\n          beat: 4,\n          id: 'd71d7f60-fa3b-4c22-8e67-4ecb5d5a870b',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'cf054f6a-c3a9-4754-8005-c920e3315b93',\n        },\n        {\n          beat: 2,\n          id: '1e9cfb91-96b9-496d-908e-84029a046582',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '3cf01e35-ade2-4786-a537-c4169c05e717',\n        },\n        {\n          beat: 4,\n          id: 'f2255cb3-8e4f-4667-9b97-21f360b20b64',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '30cefbb0-4ae1-405c-abf5-74e7bc99620d',\n        },\n        {\n          beat: 4,\n          id: 'e942a593-39b2-40c3-a3cd-72dbd81c0873',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: 'be3eeed2-0522-4d07-b055-c80b06b16020',\n        },\n        {\n          beat: 4,\n          id: '25d9b2dd-3f45-4e50-9b4f-6bb1a806b2a7',\n        },\n      ],\n    ],\n    'c068fa91-9977-4fb1-9f41-ec3fe8473cea': [\n      [\n        {\n          beat: 1.5,\n          id: '57a9a0f6-d577-4381-9e91-df55be6c6c26',\n        },\n        {\n          beat: 2.5,\n          id: 'f97b8fee-49cd-41b0-9a9f-ea006edbaae3',\n        },\n        {\n          beat: 3.5,\n          id: '1067fcad-8958-4712-bced-095f6210e36a',\n        },\n        {\n          beat: 4.5,\n          id: 'f8e73df5-a57a-4928-a941-69411c0bd609',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'cb533ea2-4485-43e9-ab9d-b62d23eeadb9',\n        },\n        {\n          beat: 2.5,\n          id: 'b7ae837e-1ea8-47bf-9e7b-4ffd6572b9bb',\n        },\n        {\n          beat: 3.5,\n          id: 'c62a9f34-4f8b-47ad-9548-7923fea09b9f',\n        },\n        {\n          beat: 4.5,\n          id: '368904f3-84b8-43cd-aa78-74e518ff3b6e',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: 'd50a7e07-0a86-43da-bbce-4989d9242bf2',\n        },\n        {\n          beat: 1.75,\n          id: '7756766c-ee9a-423a-962d-53e9a4a08ab0',\n        },\n        {\n          beat: 2.5,\n          id: 'bb1ad65f-8365-4475-b5f4-554d20a925b2',\n        },\n        {\n          beat: 2.75,\n          id: '4cc38af7-889a-4868-ac4d-071637b45512',\n        },\n        {\n          beat: 3.5,\n          id: '02cbff37-4de2-420c-b183-c3e916749788',\n        },\n        {\n          beat: 3.75,\n          id: '8ef297a9-c1d0-40db-8e8c-cdbefe4ea651',\n        },\n        {\n          beat: 4.5,\n          id: '3155ca15-d2f0-4faa-bd8b-7b54d61ac6bc',\n        },\n        {\n          beat: 4.75,\n          id: '4c3235c6-8cad-4bc6-882f-2bbbe9f900ef',\n        },\n      ],\n      [\n        {\n          beat: 1.25,\n          id: '95b23210-ecd8-4861-9ece-797717a2ed77',\n        },\n        {\n          beat: 1.75,\n          id: '5743bde4-4f27-45b9-923c-128f73c39c3a',\n        },\n        {\n          beat: 3.25,\n          id: '458362b7-9587-4b1f-960a-8736506c5504',\n        },\n        {\n          beat: 3.5,\n          id: 'd5c04c09-91c7-4653-ad33-203c99fe8e4d',\n        },\n        {\n          beat: 3.75,\n          id: '63b7aa22-a55b-4c13-ac43-f48b64ee418f',\n        },\n        {\n          beat: 4.25,\n          id: 'b3d7000b-72b6-4d25-8aa3-3a874bb385ef',\n        },\n        {\n          beat: 4.5,\n          id: '503375ea-f43c-468a-ad87-af11412d9a2f',\n        },\n        {\n          beat: 4.75,\n          id: '80b0e871-3a6d-4572-8428-39eb19cd3bfb',\n        },\n        {\n          beat: 1.5,\n          id: '2f925e26-784b-4fec-9968-5b925cb24f8d',\n        },\n        {\n          beat: 2.25,\n          id: '5b20b66b-10f7-49c1-ad42-3ef2f294cd24',\n        },\n        {\n          beat: 2.75,\n          id: '9ab8f5e5-31fa-4598-a41b-560ce212ec50',\n        },\n        {\n          beat: 2.5,\n          id: '8439ba8f-1ba8-4503-95c8-a9ab0454f05c',\n        },\n      ],\n      [\n        {\n          beat: 1.25,\n          id: '820057ba-35fb-4c79-abeb-f06b2ffaeca3',\n        },\n        {\n          beat: 1.5,\n          id: '425f77ca-eee7-4d67-880e-fd8ed9216fbe',\n        },\n        {\n          beat: 1.75,\n          id: '4726fe9d-477b-4b06-b76d-c36897bcaeaa',\n        },\n        {\n          beat: 2.25,\n          id: 'f1adb17a-308a-4512-9ce1-036173517e48',\n        },\n        {\n          beat: 2.5,\n          id: '88762626-761f-4fb6-866b-408e0bf64541',\n        },\n        {\n          beat: 2.75,\n          id: '7f37b1f1-6421-431c-a624-a0dca86ba2ca',\n        },\n        {\n          beat: 3.25,\n          id: 'c101fb88-ddf6-4725-a543-08fd2573385d',\n        },\n        {\n          beat: 3.5,\n          id: 'db6127e2-ac00-4ff2-adb8-25ac13212792',\n        },\n        {\n          beat: 4,\n          id: '8bce9e4f-f1c8-4857-baa2-3634f3e1f8a1',\n        },\n        {\n          beat: 4.25,\n          id: 'fd8d21bb-1cb1-4198-94d7-8315cd5850ae',\n        },\n        {\n          beat: 4.5,\n          id: 'f3083a12-d5d1-4319-b291-a0c25f3d18cd',\n        },\n        {\n          beat: 4.75,\n          id: '30670921-d80f-4ad8-b672-d68bd87c68ea',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: 'ab8073ab-176c-4ca5-a879-20ee3dbea4a1',\n        },\n        {\n          beat: 2,\n          id: 'd4fb2c83-47bc-4bcf-ad5b-f722b24bba2f',\n        },\n        {\n          beat: 2.5,\n          id: '8ce200de-203b-4c12-831d-63013b4b3102',\n        },\n        {\n          beat: 3,\n          id: '243963db-7c85-4e61-b715-e485c3cc155f',\n        },\n        {\n          beat: 3.5,\n          id: '5220a19d-1a8f-4428-a11f-54ea0c3f95ba',\n        },\n        {\n          beat: 4.5,\n          id: '6ab1ccd8-e95c-4791-ba40-a14ebb477f67',\n        },\n      ],\n      [\n        {\n          beat: 1.5,\n          id: '57003873-ca6f-4554-ae80-462a7d4df020',\n        },\n        {\n          beat: 1.75,\n          id: '6531ed77-f7fb-4735-bbe2-967fa28e35fb',\n        },\n        {\n          beat: 2.5,\n          id: '3ab75a14-6ca6-4fcf-a801-fa0ff7c8f280',\n        },\n        {\n          beat: 2.75,\n          id: 'b1d347d9-b22e-468f-b7c0-44ff09b436ed',\n        },\n        {\n          beat: 3.5,\n          id: 'a5ba160e-fc4a-4df3-a1f2-1408ce0e90f1',\n        },\n        {\n          beat: 3.75,\n          id: 'dab5bf93-eef3-4fab-9781-86f07508ad2a',\n        },\n        {\n          beat: 4.5,\n          id: '434c58cb-4bba-48aa-9bda-fa1ecb5b5d89',\n        },\n        {\n          beat: 4.75,\n          id: 'b5a2db63-2aae-46ee-9950-abfffb78f84a',\n        },\n      ],\n    ],\n    '4ed97fa7-8798-4cf6-8c7b-0e42c76f1612': [\n      [\n        {\n          beat: 1,\n          id: '8ac39cd2-9fa9-496f-b715-17127eb5ca1e',\n        },\n        {\n          beat: 2,\n          id: 'f5e65dcb-8ed4-438e-b82e-6d515b41778d',\n        },\n        {\n          beat: 3,\n          id: 'fa570e68-b4b4-4845-a57b-52ebb2466ea3',\n        },\n        {\n          beat: 4,\n          id: '7efb37f5-2f0c-471d-b45c-88982e1e5313',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'd3dcb8fa-f1be-4f24-9dc7-7eae74b8bf7d',\n        },\n        {\n          beat: 2,\n          id: 'ba2fc358-76f3-4e29-ad7d-af49262990f9',\n        },\n        {\n          beat: 3,\n          id: 'a170429d-8d3d-4b73-81c7-eb30bfc11894',\n        },\n        {\n          beat: 4,\n          id: '51fdcc1d-6a80-45c6-8679-69b346581c71',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '780939be-af1d-4090-a9bf-ce4ce9704ba4',\n        },\n        {\n          beat: 2,\n          id: '369aa28e-56e7-4c3b-82a0-0f4ea8824342',\n        },\n        {\n          beat: 3,\n          id: 'cec4c74a-684d-47c3-8179-20b248843dd3',\n        },\n        {\n          beat: 4,\n          id: '68d5aaf2-01e1-4554-8068-a49854e3463b',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '1c818f8d-d82e-43c5-b786-b4698646ad9d',\n        },\n        {\n          beat: 2,\n          id: 'd53c1d33-5e6b-434e-97bc-24f63ae3a0ff',\n        },\n        {\n          beat: 3,\n          id: '7fb65445-5480-4c9a-b648-1411402d6cae',\n        },\n        {\n          beat: 4,\n          id: 'd940e016-b171-40b5-978c-b095c4b81139',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '2ce68236-9d55-4b07-8325-36e11642bef5',\n        },\n        {\n          beat: 2,\n          id: '328e608e-5e7b-4b61-9855-b7972f9ca391',\n        },\n        {\n          beat: 3,\n          id: '861aae24-bcab-4ee6-ad64-1dcaeda79aba',\n        },\n        {\n          beat: 4,\n          id: 'e1ac4617-7ac4-4103-81d7-0dbef5f46c03',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '3e597539-9b7a-4122-8ce5-6be97dec9eee',\n        },\n        {\n          beat: 2,\n          id: '243d3858-3c81-45c3-bdd5-b241d34274dd',\n        },\n        {\n          beat: 3,\n          id: 'b6612d76-e85b-4c76-a956-d09c2a7d242a',\n        },\n        {\n          beat: 3.75,\n          id: 'b9587a06-60c0-48c8-92d4-654a93a7f187',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '3e6480dc-e080-4da9-a705-95e2ffb8120b',\n        },\n        {\n          beat: 4,\n          id: 'ff253c70-0929-48bf-91d3-55369e57acf4',\n        },\n      ],\n      [],\n    ],\n    '7ac094ef-282c-4d9d-8d7c-390118dd925a': [\n      [],\n      [\n        {\n          beat: 1,\n          id: '3dbfc617-d4d8-43b2-8553-ad4c394f3468',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 3,\n          id: '54ef53d6-4c41-4502-b402-92de238ae13c',\n        },\n        {\n          beat: 4.5,\n          id: '9f16fcca-a4b6-4989-9b79-d0209d30fba4',\n        },\n      ],\n      [],\n      [],\n      [],\n    ],\n    '046c982a-2576-453d-b021-963c6d3076ee': [\n      [],\n      [\n        {\n          beat: 1.75,\n          id: '15914063-1610-4045-8741-ca4ef89aa9ae',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 3.5,\n          id: 'd24fe24f-4816-49fc-8d80-786f2a81899b',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 3.5,\n          id: '4e149455-6607-4182-8b32-62bc0504910c',\n        },\n      ],\n      [],\n    ],\n    '12a11640-05d9-4a1e-88ff-c4c7525385b1': [\n      [],\n      [\n        {\n          beat: 2.5,\n          id: 'bb83c91a-1113-4a22-86a0-f82d5f79fb75',\n        },\n        {\n          beat: 3.5,\n          id: 'f1119e47-3fa6-4252-a2da-9f1f69fda753',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 3.75,\n          id: '16f782bc-9119-4027-a255-19ac4b48ace4',\n        },\n        {\n          beat: 4.5,\n          id: 'c68bc580-927f-407c-be0e-5dbbfb9db1d1',\n        },\n      ],\n      [\n        {\n          beat: 3.5,\n          id: '47dc9fce-95b7-4251-8551-0647baa5e37a',\n        },\n      ],\n      [\n        {\n          beat: 4.5,\n          id: 'd8fef37d-24a3-4159-969d-5150903be141',\n        },\n      ],\n      [],\n    ],\n    'e72fec64-9e3d-4848-81ee-c35643a70624': [\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '3225b1df-8e08-4f63-9ba5-90a5fc8f6ffe',\n        },\n        {\n          beat: 1.5,\n          id: '38ce24c2-c238-45c3-8253-fa17b059e8c3',\n        },\n        {\n          beat: 2,\n          id: 'f9088de3-375f-4412-92b4-0e2764693657',\n        },\n        {\n          beat: 2.25,\n          id: '9fcefcd5-5724-404a-80d6-11c2e92cdae8',\n        },\n        {\n          beat: 2.75,\n          id: 'b061538f-361d-42d0-a3c4-8a48cabb7fa8',\n        },\n        {\n          beat: 3.5,\n          id: '5dbb95e8-8218-40b4-8a93-13ac7d45acd6',\n        },\n        {\n          beat: 4,\n          id: 'bbcf5992-8742-4e4b-9ddd-95c4e6d81ea7',\n        },\n        {\n          beat: 4.5,\n          id: 'd39c2d9a-3441-480b-9474-d7bfa171d08a',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '6b32ce50-6709-498a-a461-08d54d14f759',\n        },\n        {\n          beat: 1.5,\n          id: '6445067d-d845-4b11-a102-44abdc5284d6',\n        },\n        {\n          beat: 2.25,\n          id: '44c90554-c61a-443a-a64a-772929c33e07',\n        },\n        {\n          beat: 3,\n          id: '80fc0e9d-6e9e-4634-8a54-3d054c5b84ff',\n        },\n        {\n          beat: 3.75,\n          id: 'dba2568f-738c-4766-8938-f777d8a908b3',\n        },\n        {\n          beat: 4.5,\n          id: '21f4d57d-2044-45a8-9b9c-b415e6e55c41',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '3ba9c1c3-6d7c-420c-80a0-465984c9cb18',\n        },\n        {\n          beat: 2,\n          id: '857b1252-951e-40c1-a889-33862385bf54',\n        },\n        {\n          beat: 3,\n          id: '5447729c-133b-4d05-be12-f0bc42d31c5e',\n        },\n        {\n          beat: 4,\n          id: '9a7dfc2e-4921-4a9b-b0b5-2a37c66926ec',\n        },\n      ],\n      [],\n    ],\n    '06126c09-a20b-4cf4-8c05-a812d3ebc7c8': [\n      [],\n      [],\n      [\n        {\n          beat: 2,\n          id: '65b0d5df-89c2-45ff-b551-6887c52643a4',\n        },\n        {\n          beat: 4,\n          id: 'd6c0e4d0-f1e1-4776-b2ce-67046a2a3c93',\n        },\n      ],\n      [],\n      [],\n      [\n        {\n          beat: 1.5,\n          id: 'beb5b7b4-a01f-4927-ab19-01a0a0e247cd',\n        },\n        {\n          beat: 2.5,\n          id: '67ffddf1-db5c-42f0-9291-2dde75fbc25f',\n        },\n        {\n          beat: 3,\n          id: '36cb3941-4d89-48c0-b172-a472797860d6',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 2,\n          id: '4f08dd26-2747-48cf-aca0-7ba192b4ec61',\n        },\n        {\n          beat: 4,\n          id: '56530e31-f07c-42ee-806e-35c27ec3480c',\n        },\n      ],\n    ],\n    'c4b37dfa-8f2f-491b-942a-b271b7b24c71': [\n      [],\n      [],\n      [\n        {\n          beat: 4,\n          id: '121cb8e8-c5d1-44ce-90dd-5851da41a1af',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: 'cd2d7c62-5c04-47c2-b353-05ec4c01b8e6',\n        },\n      ],\n      [],\n      [\n        {\n          beat: 3.75,\n          id: '93701ee1-8442-4c34-8ec8-6232ce1ddff5',\n        },\n      ],\n      [\n        {\n          beat: 2.5,\n          id: '93caca6b-7ead-4c3c-b0eb-47faed44a9ab',\n        },\n      ],\n      [],\n    ],\n    '3bd51432-6e07-4ec5-93b0-04eef8be49f6': [\n      [],\n      [],\n      [],\n      [],\n      [],\n      [\n        {\n          beat: 1,\n          id: '3456eb7c-091d-4698-8b66-5482a5ee2fbf',\n        },\n        {\n          beat: 3,\n          id: '3c063e4b-361c-4dda-8df3-1ff3da9c7e95',\n        },\n      ],\n      [\n        {\n          beat: 2,\n          id: '324da79d-4cde-45c7-8d6b-5775d0fdd45b',\n        },\n        {\n          beat: 4,\n          id: 'a1da337e-799d-445f-8670-baf480620420',\n        },\n      ],\n      [\n        {\n          beat: 1,\n          id: '506317dc-0772-43af-82c1-17dd28fe50e7',\n        },\n        {\n          beat: 2,\n          id: '1e50f328-7070-4a69-be19-bba1649ffbd4',\n        },\n        {\n          beat: 3,\n          id: '8343c55e-a041-43fa-8615-878057fe237a',\n        },\n        {\n          beat: 4,\n          id: '52b072fb-eb3e-410f-906c-feedbf77faea',\n        },\n      ],\n    ],\n  },\n};\n"
  },
  {
    "path": "src/reducer.js",
    "content": "import { combineReducers } from 'redux';\nimport {\n  channelsReducer,\n  playbackSessionReducer,\n  tempoReducer,\n  masterReducer,\n  notesReducer,\n  presetsReducer,\n  windowReducer,\n  userSamplesReducer,\n} from './common';\n\nexport default combineReducers({\n  channels: channelsReducer,\n  playbackSession: playbackSessionReducer,\n  tempo: tempoReducer,\n  master: masterReducer,\n  notes: notesReducer,\n  presets: presetsReducer,\n  window: windowReducer,\n  userSamples: userSamplesReducer,\n});\n"
  },
  {
    "path": "src/samples.config.js",
    "content": "import tr707Bd from './assets/drums/707/707-bd.mp3';\nimport tr707SdLow from './assets/drums/707/707-sd-low.mp3';\nimport tr707SdHigh from './assets/drums/707/707-sd-high.mp3';\nimport tr707Ch from './assets/drums/707/707-ch.mp3';\nimport tr707Oh from './assets/drums/707/707-oh.mp3';\nimport tr707Clap from './assets/drums/707/707-clap.mp3';\nimport tr707Tamb from './assets/drums/707/707-tamb.mp3';\nimport tr808BdShort from './assets/drums/808/808-bd-short.mp3';\nimport tr808BdLong from './assets/drums/808/808-bd-long.mp3';\nimport tr808Cowbell from './assets/drums/808/808-cowbell.mp3';\nimport tr808Sd from './assets/drums/808/808-sd.mp3';\nimport tr808Clap from './assets/drums/808/808-clap.mp3';\nimport tr808Ch from './assets/drums/808/808-ch.mp3';\nimport tr808Oh from './assets/drums/808/808-oh.mp3';\nimport tr808Cym from './assets/drums/808/808-cym.mp3';\nimport tr808Clav from './assets/drums/808/808-clav.mp3';\nimport tr808Rs from './assets/drums/808/808-rs.mp3';\nimport tr808Ht from './assets/drums/808/808-ht.mp3';\nimport tr808Mt from './assets/drums/808/808-mt.mp3';\nimport tr808Lt from './assets/drums/808/808-lt.mp3';\nimport AcetoneBd from './assets/drums/acetone/acetone-bd.mp3';\nimport AcetoneSd1 from './assets/drums/acetone/acetone-sd-1.mp3';\nimport AcetoneSd2 from './assets/drums/acetone/acetone-sd-2.mp3';\nimport AcetoneCh from './assets/drums/acetone/acetone-ch.mp3';\nimport AcetoneOh from './assets/drums/acetone/acetone-oh.mp3';\nimport AcetonePerc1 from './assets/drums/acetone/acetone-perc-1.mp3';\nimport AcetonePerc2 from './assets/drums/acetone/acetone-perc-2.mp3';\nimport LinnBd from './assets/drums/linndrum/linn-bd.mp3';\nimport LinnSd from './assets/drums/linndrum/linn-sd.mp3';\nimport LinnCh from './assets/drums/linndrum/linn-ch.mp3';\nimport LinnPh from './assets/drums/linndrum/linn-ph.mp3';\nimport LinnClap from './assets/drums/linndrum/linn-clap.mp3';\nimport LinnTamb from './assets/drums/linndrum/linn-tamb.mp3';\nimport LinnCowbell from './assets/drums/linndrum/linn-cowbell.mp3';\nimport LinnHt from './assets/drums/linndrum/linn-ht.mp3';\nimport LinnMt from './assets/drums/linndrum/linn-mt.mp3';\nimport LinnLt from './assets/drums/linndrum/linn-lt.mp3';\nimport LinnRim from './assets/drums/linndrum/linn-rim.mp3';\nimport HipHopBd1 from './assets/drums/hip-hop/hip-hop-bd-1.mp3';\nimport HipHopBd2 from './assets/drums/hip-hop/hip-hop-bd-2.mp3';\nimport HipHopSd1 from './assets/drums/hip-hop/hip-hop-sd-1.mp3';\nimport HipHopSd2 from './assets/drums/hip-hop/hip-hop-sd-2.mp3';\nimport HipHopCh1 from './assets/drums/hip-hop/hip-hop-ch-1.mp3';\nimport HipHopCh2 from './assets/drums/hip-hop/hip-hop-ch-2.mp3';\nimport HipHopOh from './assets/drums/hip-hop/hip-hop-oh.mp3';\n\nexport const samples = {\n  tr707Bd,\n  tr707SdLow,\n  tr707SdHigh,\n  tr707Ch,\n  tr707Oh,\n  tr707Clap,\n  tr707Tamb,\n  tr808BdShort,\n  tr808BdLong,\n  tr808Cowbell,\n  tr808Sd,\n  tr808Clap,\n  tr808Ch,\n  tr808Oh,\n  tr808Cym,\n  tr808Clav,\n  tr808Rs,\n  tr808Ht,\n  tr808Mt,\n  tr808Lt,\n  AcetoneBd,\n  AcetoneSd1,\n  AcetoneSd2,\n  AcetoneCh,\n  AcetoneOh,\n  AcetonePerc1,\n  AcetonePerc2,\n  LinnBd,\n  LinnSd,\n  LinnCh,\n  LinnPh,\n  LinnClap,\n  LinnTamb,\n  LinnCowbell,\n  LinnHt,\n  LinnMt,\n  LinnLt,\n  LinnRim,\n  HipHopBd1,\n  HipHopBd2,\n  HipHopSd1,\n  HipHopSd2,\n  HipHopCh1,\n  HipHopCh2,\n  HipHopOh,\n};\n\nconst sampleOptions = [\n  {\n    name: '707 Bass',\n    url: tr707Bd,\n  },\n  {\n    name: '707 Snare (low)',\n    url: tr707SdLow,\n  },\n  {\n    name: '707 Snare (high)',\n    url: tr707SdHigh,\n  },\n  {\n    name: '707 Hi-hat Closed',\n    url: tr707Ch,\n  },\n  {\n    name: '707 Hi-hat Open',\n    url: tr707Oh,\n  },\n  {\n    name: '707 Clap',\n    url: tr707Clap,\n  },\n  {\n    name: '707 Tambourine',\n    url: tr707Tamb,\n  },\n  {\n    name: '808 Bass Short',\n    url: tr808BdShort,\n  },\n  {\n    name: '808 Bass Long',\n    url: tr808BdLong,\n  },\n  {\n    name: '808 Cowbell',\n    url: tr808Cowbell,\n  },\n  {\n    name: '808 Snare',\n    url: tr808Sd,\n  },\n  {\n    name: '808 Clap',\n    url: tr808Clap,\n  },\n  {\n    name: '808 Hi-hat Closed',\n    url: tr808Ch,\n  },\n  {\n    name: '808 Hi-hat Open',\n    url: tr808Oh,\n  },\n  {\n    name: '808 Cymbal',\n    url: tr808Cym,\n  },\n  {\n    name: '808 Clave',\n    url: tr808Clav,\n  },\n  {\n    name: '808 Rimshot',\n    url: tr808Rs,\n  },\n  {\n    name: '808 High Tom',\n    url: tr808Ht,\n  },\n  {\n    name: '808 Mid Tom',\n    url: tr808Mt,\n  },\n  {\n    name: '808 Low Tom',\n    url: tr808Lt,\n  },\n  {\n    name: 'Ace Bass',\n    url: AcetoneBd,\n  },\n  {\n    name: 'Ace Snare (Short)',\n    url: AcetoneSd1,\n  },\n  {\n    name: 'Ace Snare (Long)',\n    url: AcetoneSd2,\n  },\n  {\n    name: 'Ace Hi-hat Closed',\n    url: AcetoneCh,\n  },\n  {\n    name: 'Ace Hi-hat Open',\n    url: AcetoneOh,\n  },\n  {\n    name: 'Ace Percussion (Low)',\n    url: AcetonePerc1,\n  },\n  {\n    name: 'Ace Percussion (High)',\n    url: AcetonePerc2,\n  },\n  {\n    name: 'LDrum Bass',\n    url: LinnBd,\n  },\n  {\n    name: 'LDrum Snare',\n    url: LinnSd,\n  },\n  {\n    name: 'LDrum Hi-hat Closed',\n    url: LinnCh,\n  },\n  {\n    name: 'LDrum Hi-hat Pressed',\n    url: LinnPh,\n  },\n  {\n    name: 'LDrum Clap',\n    url: LinnClap,\n  },\n  {\n    name: 'LDrum Tambourine',\n    url: LinnTamb,\n  },\n  {\n    name: 'LDrum Cowbell',\n    url: LinnCowbell,\n  },\n  {\n    name: 'LDrum High Tom',\n    url: LinnHt,\n  },\n  {\n    name: 'LDrum Mid Tom',\n    url: LinnMt,\n  },\n  {\n    name: 'LDrum Low Tom',\n    url: LinnLt,\n  },\n  {\n    name: 'LDrum Rimshot',\n    url: LinnRim,\n  },\n  {\n    name: 'Hip Hop Bass 1',\n    url: HipHopBd1,\n  },\n  {\n    name: 'Hip Hop Bass 2',\n    url: HipHopBd2,\n  },\n  {\n    name: 'Hip Hop Snare 1',\n    url: HipHopSd1,\n  },\n  {\n    name: 'Hip Hop Snare 2',\n    url: HipHopSd2,\n  },\n  {\n    name: 'Hip Hop Hi-hat Closed 1',\n    url: HipHopCh1,\n  },\n  {\n    name: 'Hip Hop Hi-hat Closed 2',\n    url: HipHopCh2,\n  },\n  {\n    name: 'Hip Hop Hi-hat Open',\n    url: HipHopOh,\n  },\n];\n\nexport default sampleOptions;\n"
  },
  {
    "path": "src/services/__mocks__/audioContext.js",
    "content": "export const getAudioContext = () => ({\n  currentTime: 1,\n});\n\nexport const playNote = () => new AudioBufferSourceNode();\n"
  },
  {
    "path": "src/services/__mocks__/audioRouter.js",
    "content": "export const playNote = () => {};\n"
  },
  {
    "path": "src/services/__mocks__/featureChecks.js",
    "content": "export const detuneSupported = true;\n"
  },
  {
    "path": "src/services/animations.js",
    "content": "import { getCurrentBeat } from './audioContext';\nimport { swing } from './swing';\n\nconst draw = (store) => {\n  // Get some data from redux store\n  const state = store.getState();\n  const { bpm, swing: swingAmount } = state.tempo;\n  const { playing, startTime } = state.playbackSession;\n  const currentBeat = getCurrentBeat(bpm, startTime);\n\n  // Grab all the toggles and animate them\n  const toggles = document.getElementsByClassName('wds-beat-marker');\n  for (let i = 0; i < toggles.length; i += 1) {\n    const toggle = toggles[i];\n    const { beat, active } = toggle.dataset;\n    const beatNum = parseFloat(beat);\n    const swingBeat = swing(beatNum, swingAmount);\n    const isActive = active === 'true';\n    if (\n      playing &&\n      isActive &&\n      currentBeat - swingBeat < 0.25 &&\n      currentBeat - swingBeat > 0\n    ) {\n      toggle.style.transition = 'all 0s';\n      toggle.style.opacity = '0.8';\n      toggle.style.transform = 'scale(1.3)';\n    } else {\n      toggle.style.transition = `all ${120 / bpm}s`;\n      toggle.style.opacity = 0;\n      toggle.style.transform = 'scale(1)';\n    }\n  }\n\n  window.requestAnimationFrame(() => {\n    draw(store);\n  });\n};\n\nexport const startAnimations = (store) => {\n  window.requestAnimationFrame(() => {\n    draw(store);\n  });\n};\n"
  },
  {
    "path": "src/services/audioAnalyzer.js",
    "content": "import { analyserNode } from './audioRouter';\n\nconst pcmData = new Float32Array(analyserNode.fftSize);\n\nexport function getVolume() {\n  analyserNode.getFloatTimeDomainData(pcmData);\n  let peak = 0;\n  for (const amplitude of pcmData) {\n    if (amplitude > peak) {\n      peak = amplitude;\n    }\n  }\n  return peak;\n}\n"
  },
  {
    "path": "src/services/audioContext.js",
    "content": "let audioCtx;\n\nexport const getAudioContext = () => {\n  if (typeof audioCtx === 'undefined') {\n    audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n  }\n  return audioCtx;\n};\n\nexport const getCurrentBeat = (bpm, startTime, currentTime) => {\n  const safeCurrentTime =\n    typeof currentTime === 'undefined' ? audioCtx.currentTime : currentTime;\n\n  const beatLengthSeconds = bpm / 60;\n  const currentBeat = (safeCurrentTime - startTime) * beatLengthSeconds;\n  return (currentBeat % 4) + 1;\n};\n"
  },
  {
    "path": "src/services/audioContext.test.js",
    "content": "import {\n  getCurrentBeat,\n} from './audioContext';\n\njest.mock('./featureChecks');\n\ndescribe('getCurrentBeat', () => {\n  test('should return beat 1 if startTime is the same as currentTime', () => {\n    expect(getCurrentBeat(60, 1, 1)).toBe(1);\n  });\n\n  test('should return beat 2 if currentTime is one second ahead of startTime and bpm = 60', () => {\n    expect(getCurrentBeat(60, 0, 1)).toBe(2);\n  });\n\n  test('should return beat 3 if currentTime is one second ahead of startTime and bpm = 120', () => {\n    expect(getCurrentBeat(120, 0, 1)).toBe(3);\n  });\n\n  test('should return beat 2.5 if currentTime is one second ahead of startTime and bpm = 90', () => {\n    expect(getCurrentBeat(90, 0, 1)).toBe(2.5);\n  });\n\n  test('should return beat 1 if currentTime is one second ahead of startTime and bpm = 240', () => {\n    expect(getCurrentBeat(240, 0, 1)).toBe(1);\n  });\n\n  test('should return beat 2 if currentTime is one second ahead of startTime and bpm = 300', () => {\n    expect(getCurrentBeat(300, 0, 1)).toBe(2);\n  });\n});\n"
  },
  {
    "path": "src/services/audioEngine.config.js",
    "content": "export const LOOKAHEAD = 0.2; // seconds\nexport const INTERVAL = 50; // milliseconds\n"
  },
  {
    "path": "src/services/audioLoop.js",
    "content": "import { getAudioContext, getCurrentBeat } from './audioContext';\nimport { updateChannelNodes } from './audioRouter';\nimport { scheduleNotes } from './audioScheduler';\nimport { setStartTime } from '../common';\nimport { INTERVAL } from './audioEngine.config';\n\nexport const initializeAudio = (store) => {\n  const audioCtx = getAudioContext(); // Start the clock\n  setInterval(() => {\n    const { playbackSession, tempo, channels, notes, master } =\n      store.getState();\n\n    updateChannelNodes(channels);\n\n    if (playbackSession.playing) {\n      let sT = playbackSession.startTime;\n      // Loop if we reached the end of the bar\n      const barLength = (4 * 60) / tempo.bpm;\n      if (audioCtx.currentTime > playbackSession.startTime + barLength) {\n        store.dispatch(setStartTime(playbackSession.startTime + barLength));\n        sT = playbackSession.startTime + barLength;\n      }\n\n      scheduleNotes({\n        notes,\n        channels,\n        startTime: sT,\n        tempo,\n        pattern: master.pattern,\n        currentBeat: getCurrentBeat(tempo.bpm, playbackSession.startTime),\n      });\n    }\n  }, INTERVAL);\n};\n"
  },
  {
    "path": "src/services/audioRouter.js",
    "content": "import { detuneSupported, stereoPannerSupported } from './featureChecks';\nimport { getAudioContext } from './audioContext';\nimport { loadImpulseResponse } from './reverb';\nimport impulseResponse from '../assets/impulse-responses/ruby-room.mp3';\n\nconst audioCtx = getAudioContext();\n\nconst masterOut = audioCtx.createGain();\nmasterOut.connect(audioCtx.destination);\n\nexport const analyserNode = audioCtx.createAnalyser();\nanalyserNode.smoothingTimeConstant = 0;\nmasterOut.connect(analyserNode);\n\nconst reverbNode = audioCtx.createConvolver();\nreverbNode.connect(masterOut);\nloadImpulseResponse(impulseResponse).then((impulseResponseArrayBuffer) => {\n  reverbNode.buffer = impulseResponseArrayBuffer;\n});\n\n/**\n * The channel routing is:\n *\n * Drum Sample\n *  -> Gain node\n *    -> Reverb node\n *      -> Master out\n *        -> Analyser node\n *    -> Pan node\n *      -> Master out\n *        -> Analyser node\n */\n\nconst channelGainNodes = {};\nconst channelPanNodes = {};\nconst channelReverbNodes = {};\n\nconst calculateGain = (channel, soloEnabled) => {\n  if (channel.muted) {\n    return 0;\n  }\n  if (soloEnabled && !channel.solo) {\n    return 0;\n  }\n  if (channel.gain === 'undefined') {\n    return 1;\n  }\n  return channel.gain;\n};\n\nconst updateGainNode = (channel, soloEnabled) => {\n  if (typeof channelGainNodes[channel.id] === 'undefined') {\n    // Set up a GainNode to control note volume\n    channelGainNodes[channel.id] = audioCtx.createGain();\n    channelGainNodes[channel.id].connect(channelPanNodes[channel.id]);\n\n    // Also route to reverb\n    channelGainNodes[channel.id].connect(channelReverbNodes[channel.id]);\n  }\n  channelGainNodes[channel.id].gain.setValueAtTime(\n    calculateGain(channel, soloEnabled),\n    audioCtx.currentTime,\n  );\n};\n\nconst updatePanNode = (channel) => {\n  if (stereoPannerSupported) {\n    if (typeof channelPanNodes[channel.id] === 'undefined') {\n      channelPanNodes[channel.id] = audioCtx.createStereoPanner();\n      channelPanNodes[channel.id].connect(masterOut);\n    }\n    channelPanNodes[channel.id].pan.setValueAtTime(\n      typeof channel.pan === 'undefined' ? 0 : channel.pan,\n      audioCtx.currentTime,\n    );\n  } else {\n    if (typeof channelPanNodes[channel.id] === 'undefined') {\n      channelPanNodes[channel.id] = audioCtx.createPanner();\n      channelPanNodes[channel.id].panningModel = 'equalpower';\n      channelPanNodes[channel.id].connect(masterOut);\n    }\n    const pan = typeof channel.pan === 'undefined' ? 0 : channel.pan;\n    channelPanNodes[channel.id].setPosition(pan, 0, 1 - Math.abs(pan));\n  }\n};\n\nconst updateReverbNode = (channel) => {\n  if (typeof channelReverbNodes[channel.id] === 'undefined') {\n    // Set up a GainNode to control the send volume to reverb\n    channelReverbNodes[channel.id] = audioCtx.createGain();\n    channelReverbNodes[channel.id].connect(reverbNode);\n  }\n  channelReverbNodes[channel.id].gain.setValueAtTime(\n    typeof channel.reverb === 'undefined' ? 0 : channel.reverb,\n    audioCtx.currentTime,\n  );\n};\n\nconst checkSoloEnabled = (channels) => {\n  for (let i = 0; i < channels.length; i += 1) {\n    if (channels[i].solo) {\n      return true;\n    }\n  }\n  return false;\n};\n\nexport const updateChannelNodes = (channels) => {\n  channels.forEach((channel) => {\n    updateReverbNode(channel);\n    updatePanNode(channel);\n    updateGainNode(channel, checkSoloEnabled(channels));\n  });\n};\n\nexport const playNote = (noteTime, buffer, channelID, notePitch = 0) => {\n  // Set up the AudioBufferSourceNode\n  const source = audioCtx.createBufferSource();\n  source.buffer = buffer;\n\n  // Detune if available\n  if (detuneSupported) {\n    source.detune.value = notePitch;\n  }\n\n  // Route to channel gain node\n  source.connect(channelGainNodes[channelID]);\n\n  // Connect and start\n  source.start(noteTime);\n  return source;\n};\n"
  },
  {
    "path": "src/services/audioScheduler.js",
    "content": "import { LOOKAHEAD } from './audioEngine.config';\nimport { playNote } from './audioRouter';\nimport { sampleStore } from './sampleStore';\nimport { swing } from './swing';\n\n// schedule is a lookup table of all the notes currently scheduled to be played\nconst schedule = {};\n\nexport const pitchToCents = ({ pitchCoarse = 0, pitchFine = 0 }) =>\n  Math.round(pitchCoarse * 100 + pitchFine);\n\nexport const playNoteNow = (noteChannel) => {\n  const pitch = pitchToCents(noteChannel);\n  playNote(null, sampleStore[noteChannel.sample], noteChannel.id, pitch);\n};\n\nexport const scheduleNote = (noteID, noteTime, noteChannel) => {\n  if (typeof schedule[noteID] === 'undefined') {\n    const pitch = pitchToCents(noteChannel);\n    schedule[noteID] = playNote(\n      noteTime,\n      sampleStore[noteChannel.sample],\n      noteChannel.id,\n      pitch,\n    );\n  }\n};\n\nexport const isBetween = (query, a, b) => query >= a && query < b;\n\nexport const getScheduledNotes = ({\n  channelNotes,\n  channel,\n  startTime,\n  tempo,\n  currentBeat,\n}) =>\n  channelNotes.map((note) => {\n    const lookaheadBeats = LOOKAHEAD * (tempo.bpm / 60);\n\n    const swingAmount = typeof tempo.swing === 'undefined' ? 0 : tempo.swing;\n    const swingBeat = swing(note.beat, swingAmount);\n\n    const noteTime = startTime + (swingBeat - 1) * (60 / tempo.bpm);\n    if (isBetween(note.beat, currentBeat, currentBeat + lookaheadBeats)) {\n      return {\n        id: note.id,\n        time: noteTime,\n        channel,\n      };\n    }\n    // If nearing the end of the bar, schedule notes at the start of the bar too\n    if (\n      isBetween(note.beat, currentBeat - 4, currentBeat + lookaheadBeats - 4)\n    ) {\n      return {\n        id: note.id,\n        time: startTime + ((note.beat + 3) * 60) / tempo.bpm,\n        channel,\n      };\n    }\n    // Return note objects with time: null that should not be scheduled\n    return {\n      id: note.id,\n      time: null,\n      channel,\n    };\n  });\n\nexport const scheduleNotes = ({\n  notes,\n  channels,\n  startTime,\n  pattern,\n  tempo,\n  currentBeat,\n}) => {\n  // Determine which notes need to be scheduled\n  const notesToSchedule = channels.reduce(\n    (accumulator, channel) => [\n      ...accumulator,\n      ...getScheduledNotes({\n        channelNotes: notes[channel.id][pattern], // Play the current pattern\n        channel,\n        startTime,\n        tempo,\n        currentBeat,\n      }),\n    ],\n    [],\n  );\n\n  // Schedule the notes\n  notesToSchedule.forEach((note) => {\n    if (note.time !== null) {\n      scheduleNote(note.id, note.time, note.channel);\n    } else {\n      delete schedule[note.id];\n    }\n  });\n};\n"
  },
  {
    "path": "src/services/audioScheduler.test.js",
    "content": "import {\n  isBetween,\n  getScheduledNotes,\n} from './audioScheduler';\n\njest.mock('./featureChecks');\njest.mock('./audioContext');\njest.mock('./audioRouter');\n\ndescribe('isBetween', () => {\n  test('should return true if query is between a and b', () => {\n    expect(isBetween(2, 1, 3)).toBe(true);\n  });\n\n  test('should return false if query is note between a and b', () => {\n    expect(isBetween(4, 1, 3)).toBe(false);\n  });\n});\n\ndescribe('getScheduledNotes', () => {\n  const testNotes = [\n    {\n      beat: 1,\n      id: 'foo',\n    },\n    {\n      beat: 2.5,\n      id: 'bar',\n    },\n    {\n      beat: 4.25,\n      id: 'bam',\n    },\n  ];\n\n  const scheduledNotes = getScheduledNotes({\n    channel: {\n      sample: {\n        url: '/whatever.wav',\n      },\n    },\n    channelNotes: testNotes,\n    tempo: {\n      bpm: 60,\n      swing: 0.2,\n    },\n    startTime: 0,\n    currentBeat: 1,\n  });\n\n  test('should return same number of notes', () => {\n    expect(scheduledNotes.length).toBe(testNotes.length);\n  });\n\n  test('should calculate noteTime correctly for notes in the lookahead period', () => {\n    expect(scheduledNotes[0].time).toBe(0);\n  });\n\n  test('should set noteTime to null if note should not be scheduled', () => {\n    expect(scheduledNotes[1].time).toBeNull();\n  });\n});\n"
  },
  {
    "path": "src/services/database.js",
    "content": "const DB_NAME = 'wds-1';\nconst DB_VERSION = 1;\nconst USER_SAMPLES = 'USER_SAMPLES';\n\nlet db;\n\nexport const initializeDB = () => new Promise((resolve, reject) => {\n  const request = indexedDB.open(DB_NAME, DB_VERSION);\n  request.onerror = (event) => {\n    reject(event);\n  };\n\n  request.onupgradeneeded = (event) => {\n    // Create an objectStore for this database\n    db = event.target.result;\n    db.createObjectStore(USER_SAMPLES);\n  };\n\n  request.onsuccess = (event) => {\n    db = event.target.result;\n    resolve();\n  };\n});\n\nexport const saveToDB = (myArrayBuffer, myKey) => new Promise((resolve, reject) => {\n  const trans = db.transaction([USER_SAMPLES], 'readwrite');\n  trans.objectStore(USER_SAMPLES).put(myArrayBuffer, myKey);\n  trans.onerror = (event) => {\n    reject(event);\n  };\n  trans.onsuccess = () => {\n    resolve(myKey);\n  };\n});\n\nexport const getFromDB = (myKey) => new Promise((resolve, reject) => {\n  const trans = db.transaction([USER_SAMPLES], 'readwrite');\n  const request = trans.objectStore(USER_SAMPLES).get(myKey);\n  request.onerror = (event) => {\n    reject(event);\n  };\n  request.onsuccess = () => {\n    if (request.result) {\n      resolve(request.result);\n    }\n    reject();\n  };\n});\n"
  },
  {
    "path": "src/services/featureChecks.js",
    "content": "const audioCtx = new (window.AudioContext || window.webkitAudioContext)();\nconst source = audioCtx.createBufferSource();\n\nexport const detuneSupported = typeof source.detune !== 'undefined';\n\nexport const stereoPannerSupported = typeof audioCtx.createStereoPanner !== 'undefined';\n"
  },
  {
    "path": "src/services/fileUtils.js",
    "content": "import { getAudioContext } from './audioContext';\n\nexport const fetchFile = (url) => new Promise(\n  (resolve, reject) => {\n    fetch(url).then((response) => {\n      if (response.ok) {\n        resolve(response.blob());\n      }\n      reject(new Error('Network response was not ok.'));\n    });\n  },\n);\n\nexport const decodeFile = (sampleBlob) => new Promise(\n  (resolve) => {\n    const fileReader = new FileReader();\n    fileReader.readAsArrayBuffer(sampleBlob);\n    fileReader.onloadend = () => {\n      resolve(fileReader.result);\n    };\n  },\n);\n\nexport const decodeAudio = (audioArrayBuffer) => new Promise(\n  (resolve, reject) => {\n    getAudioContext().decodeAudioData(audioArrayBuffer, resolve, reject);\n  },\n);\n"
  },
  {
    "path": "src/services/pwaInstall.js",
    "content": "import { setCanInstall } from '../common';\n\nlet deferredPrompt;\n\nexport const initializePwaInstall = (store) => {\n  window.addEventListener('beforeinstallprompt', (e) => {\n    e.preventDefault();\n    deferredPrompt = e;\n    store.dispatch(setCanInstall(true));\n  });\n\n  window.addEventListener('appinstalled', () => {\n    store.dispatch(setCanInstall(false));\n  });\n};\n\nexport const promptToInstall = () => {\n  if (typeof deferredPrompt !== 'undefined') {\n    deferredPrompt.prompt();\n  }\n};\n"
  },
  {
    "path": "src/services/reverb.js",
    "content": "import { fetchFile, decodeFile, decodeAudio } from './fileUtils';\n\nconst impulseResponses = {};\n\nexport const loadImpulseResponse = (fileName) => {\n  if (typeof impulseResponses[fileName] !== 'undefined') {\n    return Promise.resolve(impulseResponses[fileName]);\n  }\n  return fetchFile(fileName)\n    .then(decodeFile)\n    .then(decodeAudio);\n};\n"
  },
  {
    "path": "src/services/sampleStore.js",
    "content": "import { fetchFile, decodeFile, decodeAudio } from './fileUtils';\nimport { saveToDB, getFromDB } from './database';\n\nexport const sampleStore = {};\n\nexport const loadSample = (url) => {\n  if (typeof sampleStore[url] !== 'undefined') {\n    return Promise.resolve(true);\n  }\n\n  return getFromDB(url)\n    .then(decodeAudio)\n    .then((drumBuffer) => {\n      sampleStore[url] = drumBuffer;\n      return true;\n    })\n    .catch(() => fetchFile(url)\n      .then(decodeFile)\n      .then(decodeAudio)\n      .then((drumBuffer) => {\n        sampleStore[url] = drumBuffer;\n        return true;\n      })\n      .catch(() => false));\n};\n\nexport const saveToSampleStore = (file) => {\n  const id = file.name;\n  return decodeFile(file)\n    .then((myArrayBuffer) => {\n      saveToDB(myArrayBuffer, id);\n      return decodeAudio(myArrayBuffer);\n    })\n    .then((drumBuffer) => {\n      sampleStore[id] = drumBuffer;\n      return id;\n    });\n};\n"
  },
  {
    "path": "src/services/swing.js",
    "content": "export const swing = (beatTime, swingAmount) => {\n  const SWING_TIMING = 0.5; // eight note cycles\n  const MAX_SWING = 0.95;\n\n  const beatCyclePos = beatTime % SWING_TIMING;\n  const beatCyclePercentage = beatCyclePos / SWING_TIMING;\n\n  const fx = (beatCyclePercentage ** (1 - swingAmount)) * MAX_SWING; // Exponential function\n  const offset = (fx - beatCyclePercentage) * beatCyclePos;\n\n  return beatTime + offset;\n};\n"
  },
  {
    "path": "src/services/unmute.js",
    "content": "import silence from '../assets/silence.mp3';\n\nexport const unmute = () => {\n  var el = document.createElement('audio');\n  el.src = silence;\n  el.play();\n};\n"
  },
  {
    "path": "src/services/uuid.js",
    "content": "import uuidv4 from 'uuid/v4';\n\nexport const uuid = uuidv4;\n"
  },
  {
    "path": "src/store.js",
    "content": "import { createStore, applyMiddleware, compose } from 'redux';\nimport thunk from 'redux-thunk';\nimport { persistStore, persistReducer, createMigrate } from 'redux-persist';\nimport storage from 'redux-persist/lib/storage';\nimport reducer from './reducer';\n\nconst migrations = {\n  1: () => ({}),\n  2: () => ({}),\n  3: () => ({}),\n};\n\nexport const configureStore = (callback) => {\n  const persistConfig = {\n    key: 'root-v1.0.0',\n    storage,\n    version: 3,\n    blacklist: ['playbackSession', 'window'],\n    migrate: createMigrate(migrations, { debug: import.meta.env.DEV }), // eslint-disable-line\n  };\n\n  const persistedReducer = persistReducer(persistConfig, reducer);\n\n  const composeEnhancers =\n    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // eslint-disable-line\n  const store = createStore(\n    persistedReducer,\n    composeEnhancers(applyMiddleware(thunk)),\n  );\n\n  const persistor = persistStore(store, null, callback);\n\n  return {\n    store,\n    persistor,\n  };\n};\n"
  },
  {
    "path": "src/styles/globalStyles.js",
    "content": "import { injectGlobal } from 'styled-components';\nimport theme from './theme';\nimport jostMediumWoff2 from '../assets/fonts/jost-medium-webfont.woff2';\nimport jostMediumWoff from '../assets/fonts/jost-medium-webfont.woff';\nimport jostBoldWoff2 from '../assets/fonts/jost-bold-webfont.woff2';\nimport jostBoldWoff from '../assets/fonts/jost-bold-webfont.woff';\nimport jostSemiboldWoff2 from '../assets/fonts/jost-semi-webfont.woff2';\nimport jostSemiboldWoff from '../assets/fonts/jost-semi-webfont.woff';\n\nexport default () => injectGlobal`\n\n  @font-face {\n    font-family: 'Jost';\n    font-style: normal;\n    font-weight: 400;\n    src: local('Jost Medium'), local('Jost-Medium'),\n        url(${jostMediumWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */\n        url(${jostMediumWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */\n  }\n\n  @font-face {\n    font-family: 'Jost';\n    font-style: normal;\n    font-weight: 600;\n    src: local('Jost SemiBold'), local('Jost-SemiBold'),\n        url(${jostSemiboldWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */\n        url(${jostSemiboldWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */\n  }\n\n  @font-face {\n    font-family: 'Josts';\n    font-style: normal;\n    font-weight: 700;\n    src: local('Jost Bold'), local('Jost-Bold'),\n        url(${jostBoldWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */\n        url(${jostBoldWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */\n  }\n\n  html {\n    background-color: ${theme.colors.nearBlack};\n  }\n\n  * {\n    font-family: \"Jost\", \"Futura\", sans-serif;\n    font-display: swap;\n  }\n\n  body { \n    min-width: 750px;\n    box-sizing: border-box;\n  }\n\n  .bpm-text-input {\n    -moz-appearance:textfield;\n  }\n\n  /* Webkit browsers like Safari and Chrome */\n  .bpm-text-input::-webkit-inner-spin-button,\n  .bpm-text-input::-webkit-outer-spin-button {\n    -webkit-appearance: none;\n    margin: 0;\n  }\n`;\n"
  },
  {
    "path": "src/styles/theme.js",
    "content": "const colors = {\n  nearWhite: '#F2F2F8',\n  lightGray: '#C0C3C7',\n  gray: '#909599',\n  steel: '#606469',\n  darkGray: '#404449',\n  nearBlack: '#202427',\n  black80: 'rgba(0,0,0,0.8)',\n  green: '#58A291',\n  lightGreen: '#68B2A1',\n  darkGreen: '#1B806D',\n  red: '#CD545B',\n  lightRed: '#DD646B',\n  darkRed: '#633231',\n  brightRed: 'rgb(244, 83, 58)',\n  brightRed30: 'rgba(244, 83, 58, 0.3)',\n  gold: '#E6A65D',\n  yellow: 'rgb(255, 224, 71)',\n  yellow30: 'rgba(255, 224, 71, 0.3)',\n  primary: 'rgba(213,255,169,1)',\n  primaryDark: 'rgba(180,215,129,1)',\n  secondary: 'rgba(152,255,193,1)',\n  blue: '#2f85c6',\n  darkBlue: '#196096',\n};\n\nexport default {\n  fontSizes: [\n    11, 13, 14, 24, 32, 48, 64, 96, 128,\n  ],\n  space: [\n    // margin and padding\n    0, 4, 8, 16, 32, 64, 128, 256,\n  ],\n  breakpoints: ['640px', '720px', '769px', '820px', '900px', '1024px', '1200px', '1400px'],\n  colors,\n  fancyButtons: {\n    green: {\n      color: 'white',\n      backgroundColor: colors.green,\n      boxShadow: `0 0.3em ${colors.darkGreen}`,\n      '&:hover': {\n        backgroundColor: colors.lightGreen,\n      },\n      '&:active': {\n        backgroundColor: colors.lightGreen,\n        boxShadow: `0 0 ${colors.darkGreen}`,\n        transform: 'translateY(0.3em)',\n      },\n    },\n    red: {\n      color: 'white',\n      backgroundColor: colors.red,\n      boxShadow: `0 0.3em ${colors.darkRed}`,\n      '&:hover': {\n        backgroundColor: colors.lightRed,\n      },\n      '&:active': {\n        backgroundColor: colors.lightRed,\n        boxShadow: `0 0 ${colors.darkRed}`,\n        transform: 'translateY(0.3em)',\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport reactRefresh from '@vitejs/plugin-react-refresh';\n\nexport default defineConfig({\n  plugins: [reactRefresh()],\n});\n"
  }
]