[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.gitignore.io/api/node,visualstudiocode\n# Edit at https://www.gitignore.io/?templates=node,visualstudiocode\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# next.js build output\n.next\n\n# nuxt.js build output\n.nuxt\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n\n# End of https://www.gitignore.io/api/node,visualstudiocode\n\n## Custom ###\n.DS_Store\nlib/\ndocs/\ndemo/release\ndemo/_data/version.yml\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 WoodNeck\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# 📷 CSS-CAMERA\n\n<!-- BADGES -->\n![npm](https://img.shields.io/npm/v/css-camera?style=for-the-badge)\n![GitHub](https://img.shields.io/github/license/woodneck/css-camera?style=for-the-badge)\n\n<p>\n  <h4><a href=\"https://woodneck.github.io/css-camera\">🎥 Demo</a> · <a href=\"https://woodneck.github.io/css-camera/release/latest/docs/index.html\">📄 Document</a></h2>\n</p>\n\n<b>Add depth to your web page with CSS3 3D transform.</b>\n\n> This project is mostly inspired by [Keith Clark's work](https://keithclark.co.uk/labs/css-fps/).\n\n## ✨ Features\n- Movable, and Rotatable camera for your scene.\n- Can move to in front of any element in your scene, whether it has been rotated or translated.\n\n## ⚙️ Installation\n```sh\nnpm i css-camera\n# or\nyarn add css-camera\n```\n\n## 🏃 Quick Start\n```js\n// Prerequisite:\n// Create your scene as you like\nconst card = document.querySelector(\"#card\");\nconst cardButton = document.querySelector(\"#card-button\");\n\n// First, make camera\nconst camera = new CSSCamera(\"#space\");\n\n// Call its method, then update it!\ncardButton.onclick = () => {\n  camera.focus(card);\n  camera.update(2000);\n}\n```\nCheck more methods on the <a href=\"https://woodneck.github.io/css-camera/release/latest/docs/index.html\">📄API Documentation</a> page</h2>\n\n## 📜 License\n[MIT](https://github.com/WoodNeck/css-camera/blob/master/LICENSE)\n"
  },
  {
    "path": "demo/css/fpv.css",
    "content": "* {\n  box-sizing: border-box;\n  transform-style: preserve-3d;\n  font-size: 0;\n}\nhtml, body {\n  position: relative;\n  width: 100vw;\n  height: 100vh;\n  margin: 0;\n  padding: 0;\n  overflow: hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n#space {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  justify-content: center;\n}\n.zone {\n  width: 100%;\n  height: 100%;\n  display: flex;\n  position: absolute;\n  justify-content: center;\n  align-items: center;\n}\n.zone0 {\n  left: 0;\n}\n.zone1 {\n  left: 700px;\n}\n.zone2 {\n  left: 1400px;\n}\n.long-hallway {\n  width: 200px;\n  height: 200px;\n}\n.is-left {\n  left: 0%;\n  transform-origin: left;\n  transform: rotateY(90deg);\n}\n.is-right {\n  right: 0%;\n  transform-origin: right;\n  transform: rotateY(-90deg);\n}\n.wall {\n  height: 200px;\n  position: absolute;\n  border: 10px solid #333;\n  background-color: white;\n}\n.is-1200 {\n  width: 1200px;\n}\n.is-1000 {\n  width: 1000px;\n}\n.is-800 {\n  width: 800px;\n}\n.is-600 {\n  width: 600px;\n}\n.is-400 {\n  width: 400px;\n}\n.is-200 {\n  width: 200px;\n}\n.arrow {\n  transform: rotateX(90deg);\n  width: 200px;\n  font-size: 300px;\n  text-align: center;\n  position: absolute;\n  bottom: 0;\n  transform-origin: bottom;\n}\n#inst {\n  position: fixed;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-size: 72px;\n}\n"
  },
  {
    "path": "demo/css/index.css",
    "content": "* {\n  box-sizing: border-box;\n}\nhtml, body {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  overflow: hidden;\n  transform-style: preserve-3d;\n  display: flex;\n  justify-content: center;\n}\n.page-header-container {\n  position: absolute;\n  bottom: 2%;\n  display: flex;\n  justify-content: center;\n  flex-flow: column;\n}\n.page-header-wrapper {\n  display: flex;\n  flex-direction: row;\n  justify-content: center;\n  align-items: center;\n}\n.page-header-icon {\n  margin-top: auto;\n  margin-bottom: auto;\n  margin-right: 0.8rem;\n}\n.page-title {\n  color: #4a4a4a;\n}\n.page-header-buttons {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  margin-top: 1%;\n}\n.page-header-buttons .button {\n  margin: 0.3rem;\n}\n#hero .button {\n  background-color: transparent;\n}\n#space {\n  display: flex;\n  width: 100%; height: 100%;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  margin: 0; padding: 0; border: 0;\n  transform-style: preserve-3d;\n  position: relative;\n}\n#card {\n  transform-style: preserve-3d;\n  transform: rotateX(-30deg) rotateY(45deg) translate3d(-180%, 80%, -200px);\n}\n.card-youtube {\n  width: 100%; height: 100%;\n  position: absolute;\n  top: 0; bottom: 0; left: 0; right: 0;\n}\n.card-side {\n  width: 10%; height: 100%;\n  position: absolute; top: 0;\n  background: #222;\n  transform-style: preserve-3d;\n  transform-origin: 0% 50%;\n  transform: rotateY(90deg);\n}\n#code {\n  max-width: 100%;\n  transform-style: preserve-3d;\n  transform: rotateZ(30deg) rotateY(-45deg) rotateX(70deg) translate3d(100%, -40%, 0px);\n}\n#code .title {\n  text-align: center;\n  font-family: 'Nanum Pen Script', cursive;\n}\n.camera-code {\n  padding: 1rem;\n}\n#hero {\n  position: absolute;\n  top: 0; left: 0; right: 0; bottom: 0;\n  transform-style: preserve-3d;\n  transform: translateZ(2400px);\n  background-color: transparent;\n}\n#more {\n  width: 100%; min-height: 100%;\n  transform-style: preserve-3d;\n  transform: rotateX(90deg) translate3d(0, 0, -1600px);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  flex-direction: column;\n}\n#more h1 {\n  margin-top: 1.5rem;\n  position: absolute;\n  top: 0%;\n}\n#more .arrow {\n  position: absolute;\n  z-index: 1;\n}\n#more .arrow.left {\n  left: 1.5rem;\n}\n#more .arrow.right {\n  right: 1.5rem;\n}\n#demo-flicking {\n  width: 100%; height: 100%;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.eg-flick-camera {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n.demo-entry {\n  width: 70%;\n  position: absolute;\n  z-index: 0;\n  display: flex;\n  justify-content: center;\n  box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.5);\n}\n.demo-wrapper {\n  width: 100%;\n}\n.demo-wrapper .caption-wrapper {\n  width: 100%; height: 100%;\n  display: flex;\n  position: absolute;\n  top: 0%;\n  left: 0%;\n  justify-content: center;\n  align-items: center;\n}\n.demo-wrapper .demo-caption {\n  font-size: 48px;\n  color: gold;\n  background-color: rgba(0, 0, 0, 0.5);\n  font-weight: bold;\n  padding: 30px;\n  border-radius: 10px;\n}\n.demo-wrapper .demo-caption:hover {\n  color: hotpink;\n}\n"
  },
  {
    "path": "demo/css/ortho.css",
    "content": "* {\n  box-sizing: border-box;\n}\nhtml, body {\n  position: relative;\n  width: 100vw;\n  height: 100vh;\n  margin: 0;\n  padding: 0;\n  overflow: hidden;\n  transform-style: preserve-3d;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n#info {\n  position: fixed;\n  top: 12px;\n  color: gold;\n  font-size: 24px;\n}\n#info2 {\n  position: fixed;\n  bottom: 12px;\n  font-size: 24px;\n}\n#info2 a {\n  color: hotpink;\n  text-decoration: none;\n}\n#space {\n  transform-style: preserve-3d;\n  width: 640px;\n  height: 100%;\n  font-size: 0;\n}\n.tile {\n  display: inline-block;\n  background-size: cover;\n  transform-style: preserve-3d;\n  image-rendering: -moz-crisp-edges;         /* Firefox */\n  image-rendering:   -o-crisp-edges;         /* Opera */\n  image-rendering: -webkit-optimize-contrast;/* Webkit (non-standard naming) */\n  image-rendering: pixelated;\n  -ms-interpolation-mode: nearest-neighbor;  /* IE (non-standard property) */\n  margin: 0; border: 0; padding: 0;\n  box-sizing: border-box;\n  width: 64px; height: 64px;\n}\n.tile::before {\n  content: '';\n  background-size: cover;\n  transform-style: preserve-3d;\n  position: absolute;\n  width: 64px; height: 32px;\n  transform: translateZ(-32px) scaleZ(-1) rotateZ(90deg) rotateX(-90deg);\n  transform-origin: 0% 0%;\n}\n.tile::after {\n  content: '';\n  background-size: cover;\n  transform-style: preserve-3d;\n  position: absolute;\n  width: 64px; height: 32px;\n  bottom: 0;\n  transform: rotateX(90deg);\n  transform-origin: bottom;\n}\n\n.height-1 {\n  transform: translateZ(32px);\n}\n.height-2 {\n  transform: translateZ(64px);\n}\n.height-3 {\n  transform: translateZ(96px);\n}\n.height-4 {\n  transform: translateZ(128px);\n}\n.height-5 {\n  transform: translateZ(160px);\n}\n\n.grass {\n  background-color: #8AD44E;\n}\n.grass::before {\n  background-color: #5FB535;\n}\n.grass::after {\n  background-color: #3D9756;\n}\n\n.grass.patch {\n  background-image: url(../asset/patch.png);\n}\n\n.grass.grassa {\n  background-image: url(../asset/grassa.png);\n}\n.grass.grassb {\n  background-image: url(../asset/grassb.png);\n}\n.grass.grassc {\n  background-image: url(../asset/grassc.png);\n}\n.grass.grassd {\n  background-image: url(../asset/grassd.png);\n}\n.grass.grasse {\n  background-image: url(../asset/grasse.png);\n}\n.grass.grassf {\n  background-image: url(../asset/grassf.png);\n}\n.grass.grassg {\n  background-image: url(../asset/grassg.png);\n}\n.grass.grassh {\n  background-image: url(../asset/grassh.png);\n}\n.grass.grassi {\n  background-image: url(../asset/grassi.png);\n}\n\n.grass.river-top {\n  background-image: url(../asset/rivertop.gif);\n}\n.grass.river-top-left {\n  background-image: url(../asset/rivertopleft.gif);\n}\n.grass.river-top-right {\n  background-image: url(../asset/rivertopleft.gif);\n  transform: scaleX(-1);\n}\n.grass.river-bottom {\n  background-image: url(../asset/riverbottom.gif);\n}\n.grass.river-bottom-left {\n  background-image: url(../asset/riverbottomleft.gif);\n}\n.grass.river-bottom-right {\n  background-image: url(../asset/riverbottomleft.gif);\n  transform: scaleX(-1);\n}\n\n.grass.road-top {\n  background-image: url(../asset/roadtop.png);\n}\n.grass.road-vertical {\n  background-image: url(../asset/roadmiddle.png);\n}\n.grass.road-bottom {\n  background-image: url(../asset/roadbottom.png);\n}\n.grass.road-left {\n  background-image: url(../asset/roadtop.png);\n  transform: rotateZ(-90deg);\n}\n.grass.road-horizontal {\n  background-image: url(../asset/roadmiddle.png);\n  transform: rotateZ(-90deg);\n}\n.grass.road-right {\n  background-image: url(../asset/roadbottom.png);\n  transform: rotateZ(-90deg);\n}\n\n.grass div {\n  position: absolute;\n  transform-origin: bottom;\n  background-size: cover;\n}\n\n.barrel {\n  width: 64px; height: 64px;\n  transform: translateZ(-16px) rotateX(-90deg);\n  background-image: url(../asset/barrel.png);\n  left: -25%; bottom: 50%;\n}\n.rock {\n  width: 64px; height: 64px;\n  transform: translateZ(-16px) rotateX(-90deg);\n  background-image: url(../asset/rock.png);\n  left: 0px; bottom: 50%;\n}\n.sign {\n  width: 64px; height: 64px;\n  transform: translateZ(-20px) rotateX(-90deg);\n  background-image: url(../asset/sign.png);\n  left: 0px; bottom: 50%;\n}\n.bush {\n  width: 64px; height: 64px;\n  transform: translateZ(-12px) rotateX(-90deg);\n  background-image: url(../asset/bush.gif);\n  left: 0px; bottom: 50%;\n}\n.fence {\n  width: 64px; height: 64px;\n  transform: translateZ(-18px) rotateX(-90deg);\n  background-image: url(../asset/fence.png);\n  left: 0px; bottom: 50%;\n}\n.tree {\n  width: 192px; height: 192px;\n  transform: rotateX(-90deg);\n  background-image: url(../asset/tree.gif);\n  left: -64px; bottom: 0px;\n}\n.flower {\n  width: 64px; height: 64px;\n  transform: translateZ(-18px) rotateX(-90deg);\n  background-image: url(../asset/flower.gif);\n  left: 0px; bottom: 50%;\n}\n.chopped {\n  width: 64px; height: 64px;\n  transform: translateZ(-2px) rotateX(-90deg);\n  background-image: url(../asset/chopped.gif);\n  left: 0px; bottom: 50%;\n}\n"
  },
  {
    "path": "demo/fpv.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>css-camera</title>\n    <meta charsset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">\n    <meta property=\"og:title\" content=\"css-camera\" />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:url\" content=\"https://woodneck.github.io/css-camera/\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/fpv.css\">\n    <script src=\"./release/latest/lib/css-camera.pkgd.js\"></script>\n  </head>\n  <body>\n    <div id=\"space\">\n      <div class=\"zone zone0\">\n        <div class=\"long-hallway\">\n          <div class=\"arrow\">↑</div>\n          <div class=\"wall is-200\"></div>\n          <div class=\"wall is-left is-1200\" style=\"background: linear-gradient( to right, white 800px, cyan )\"></div>\n          <div class=\"wall is-right is-1000\" style=\"background: linear-gradient( to left, white 800px, magenta )\"></div>\n          <div class=\"wall is-600\" style=\"left: 0; transform: translateZ(-1200px); background: linear-gradient( to right, cyan, white, yellow );\"></div>\n          <div class=\"wall is-200\" style=\"left: 200px; transform: translateZ(-1000px); background: magenta;\"></div>\n          <div class=\"wall is-right is-600\" style=\"transform: translateZ(-600px) rotateY(-90deg) translateZ(-400px); background: linear-gradient( to left, magenta, white, yellow )\"></div>\n          <div class=\"wall is-right is-200\" style=\"transform: translateZ(-800px) rotateY(-90deg) translateZ(-200px); background: linear-gradient( to right, magenta, white, cyan )\"></div>\n          <div class=\"wall\" style=\"width: 398px; border-left: none; transform: translateX(202px) translateZ(-600px); background: linear-gradient( to left, magenta, white 180px)\"></div>\n        </div>\n      </div>\n      <div class=\"zone zone1\">\n        <div style=\"width: 200px; height: 200px; transform: translateZ(-600px);\">\n          <div class=\"wall is-600\" style=\"left: 0; background: linear-gradient( to right, white 400px, magenta );\"></div>\n          <div class=\"wall is-left is-600\" style=\"background: linear-gradient( to right, white, white, cyan );\"></div>\n          <div class=\"wall is-600\" style=\"transform: translateZ(-600px); background: linear-gradient( to left, yellow, white, cyan );\"></div>\n          <div class=\"wall is-right is-600\" style=\"transform: rotateY(-90deg) translateZ(-400px); background: linear-gradient( to right, yellow, white, magenta );\"></div>\n          <div class=\"wall is-200\" style=\"transform: translateX(200px) translateZ(-400px); background: magenta;\"></div>\n          <div class=\"wall is-200\" style=\"transform: translateX(200px) translateZ(-200px); background: cyan;\"></div>\n          <div class=\"wall is-left is-200\" style=\"transform: translateZ(-200px) rotateY(90deg) translateZ(200px); background: linear-gradient( to left, magenta, white, cyan);\"></div>\n          <div class=\"wall is-right is-200\" style=\"transform: translateZ(-200px) rotateY(-90deg) translateZ(-200px); background: linear-gradient( to right, magenta, white, cyan);\"></div>\n        </div>\n      </div>\n      <div class=\"zone zone2\">\n        <div style=\"width: 200px; height: 200px; transform: translateZ(-600px);\">\n          <div class=\"wall is-600\" style=\"left: 0; background: linear-gradient( to right, white 400px, magenta );\"></div>\n          <div class=\"wall is-left is-1200\" style=\"background: linear-gradient( to right, white, white, cyan 600px)\"></div>\n          <div class=\"wall\" style=\"width: 398px; border-left: none; transform: translateX(202px) translateZ(-600px); background: linear-gradient( to left, yellow, white, cyan );\"></div>\n          <div class=\"wall is-right is-600\" style=\"transform: rotateY(-90deg) translateZ(-400px); background: linear-gradient( to right, yellow, white, magenta );\"></div>\n          <div class=\"wall is-200\" style=\"transform: translateZ(-1200px); background: url(./asset/winner.png); background-position: center; background-repeat: no-repeat; background-size: cover; padding: 10%; background-origin: content-box;\"></div>\n          <div class=\"wall is-200\" style=\"transform: translateX(200px) translateZ(-200px); background: cyan;\"></div>\n          <div class=\"wall is-left is-1000\" style=\"backface-visibility: hidden; transform: translateZ(-1200px) translateX(200px) rotateY(-90deg); background: cyan;\"></div>\n          <div class=\"wall is-right is-200\" style=\"transform: translateZ(-200px) rotateY(-90deg) translateZ(-200px); background: linear-gradient( to right, magenta, white, cyan);\"></div>\n        </div>\n      </div>\n    </div>\n    <div id=\"inst\">Click the screen to control</div>\n    <script src=\"js/fpv.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>css-camera</title>\n    <meta charsset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">\n    <meta property=\"og:title\" content=\"css-camera\" />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:url\" content=\"https://woodneck.github.io/css-camera/\" />\n    <script defer src=\"https://use.fontawesome.com/releases/v5.3.1/js/all.js\"></script>\n    <link href=\"https://fonts.googleapis.com/css?family=Nanum+Pen+Script&display=swap\" rel=\"stylesheet\">\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css\" integrity=\"sha256-ujE/ZUB6CMZmyJSgQjXGCF4sRRneOimQplBVLu8OU5w=\" crossorigin=\"anonymous\" />\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.15.9/build/styles/an-old-hope.min.css\">\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/index.css\">\n    <script src=\"https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.15.9/build/highlight.min.js\"></script>\n    <script src=\"https://naver.github.io/egjs-flicking/release/latest/dist/flicking.pkgd.min.js\"></script>\n    <script src=\"./release/latest/lib/css-camera.pkgd.js\"></script>\n    <script>hljs.initHighlightingOnLoad();</script>\n  </head>\n  <body>\n    <div id=\"space\">\n      <section id=\"hero\" class=\"hero is-white is-fullheight\">\n        <div class=\"hero-body\">\n          <div class=\"container has-text-centered\">\n            <h1 class=\"title\"><i class=\"fas fa-camera-retro\"></i> CSS-CAMERA</h1>\n            <h2 class=\"subtitle\">Add depth to your web page with CSS3 3D transform.</h2>\n            <a class=\"button is-white\" href=\"./release/latest/docs/index.html\">\n              <span class=\"icon\"><i class=\"fas fa-book\"></i></span>\n              <span>Documents</span>\n            </a>\n            <a class=\"button is-white\" href=\"https://github.com/WoodNeck/css-camera\">\n              <span class=\"icon\"><i class=\"fab fa-github\"></i></span>\n              <span>Github</span>\n            </a>\n          </div>\n        </div>\n        <div class=\"container is-fluid\">\n          <div class=\"container\">\n            <h1 class=\"title\"><i class=\"fas fa-cog\"></i> Install</h1>\n            <div class=\"notification\">$ npm i css-camera</div>\n          </div>\n        </div>\n      </section>\n      <div id=\"card\" class=\"container\">\n        <div class=\"card-wrapper\">\n          <div class=\"card\">\n            <div class=\"card-image\">\n              <figure class=\"image is-4by3\">\n                <iframe class=\"card-youtube\" src=\"https://www.youtube.com/embed/AckybSTFSXo\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n              </figure>\n            </div>\n            <div class=\"card-content\">\n              <div class=\"media\">\n                <div class=\"media-left\">\n                  <figure class=\"image is-48x48\">\n                    <img src=\"https://avatars0.githubusercontent.com/u/26213435?s=460&v=4\" alt=\"Placeholder image\">\n                  </figure>\n                </div>\n                <div class=\"media-content\">\n                  <p class=\"title is-4\">WoodNeck</p>\n                  <p class=\"subtitle is-6\">@woodneck</p>\n                </div>\n              </div>\n\n              <div class=\"content\">\n                You can put any HTML element you like, then just transform it.\n                <a href=\"https://github.com/WoodNeck/css-camera\">@css-camera</a> will do the rest for you.\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n      <div id=\"code\" class=\"container\">\n        <h1 class=\"title\">It's super simple!</h1>\n        <pre><code class=\"camera-code js\">// Prerequisite:\n// Create your scene as you like\nconst card = document.querySelector(\"#card\");\nconst cardButton = document.querySelector(\"#card-button\");\n\n// First, make camera\nconst camera = new CSSCamera(\"#space\");\n\n// Call its method, then update it!\ncardButton.onclick = () => {\n  // Move camera in front of \"card\"\n  camera.focus(card);\n  camera.update(2000);\n}</code></pre>\n        <h1 class=\"title is-size-5-mobile\">See the <a href=\"https://github.com/WoodNeck/css-camera/blob/master/demo/js/index.js\">source</a> of this page.</h1>\n      </div>\n\n      <div id=\"more\" class=\"container\">\n        <h1 class=\"title has-text-centered\">More demos?</h1>\n        <div id=\"demo-flicking\" class=\"container\">\n          <div class=\"demo-entry\">\n            <div class=\"demo-wrapper\">\n              <figure class=\"image is-4by3\">\n                <img class=\"demo-thumb\" src=\"./asset/ortho-thumb.png\" />\n              </figure>\n              <div class=\"caption-wrapper\">\n                <a class=\"demo-caption is-size-5-mobile\" href=\"./ortho.html\">\n                  <span>Orthographic projection</span>\n                </a>\n              </div>\n            </div>\n          </div>\n          <div class=\"demo-entry\" style=\"left: calc(85% + 50px)\">\n            <div class=\"demo-wrapper\">\n              <figure class=\"image is-4by3\">\n                <img class=\"demo-thumb\" src=\"./asset/fpv-thumb.png\" />\n              </figure>\n              <div class=\"caption-wrapper\">\n                <a class=\"demo-caption is-size-5-mobile\" href=\"./fpv.html\">\n                  <span>First person view demo</span>\n                </a>\n              </div>\n            </div>\n          </div>\n        </div>\n        <div class=\"arrow left\"><i class=\"fas fa-arrow-left\"></i></div>\n        <div class=\"arrow right\"><i class=\"fas fa-arrow-right\"></i></div>\n      </div>\n    </div>\n    <header class=\"page-header-container\">\n      <div class=\"page-header-buttons\">\n        <span>🎥</span>\n        <a id=\"hero-button\" class=\"button is-link\">Main</a>\n        <a id=\"card-button\" class=\"button is-link is-outlined\">Card</a>\n        <a id=\"code-button\" class=\"button is-link is-outlined\">Code</a>\n        <a id=\"more-button\" class=\"button is-link is-outlined\">More</a>\n      </div>\n    </header>\n    <script src=\"js/index.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/js/fpv.js",
    "content": "const windowHeight = window.innerHeight;\nconst camera = new CSSCamera(\"#space\", {\n  position: [0, 0, -10],\n  perspective: windowHeight / 2,\n});\n\nlet zone = 0;\nlet rotate = 0;\nconst inst = document.querySelector(\"#inst\");\n\ndocument.documentElement.onclick = function() {\n  document.documentElement.requestPointerLock();\n}\ndocument.addEventListener('pointerlockchange', onLockChange, false);\ndocument.addEventListener('mozpointerlockchange', onLockChange, false);\n\nfunction onLockChange() {\n  if (document.pointerLockElement === document.documentElement ||\n      document.mozPointerLockElement === document.documentElement) {\n    inst.style.display = \"none\";\n    document.addEventListener(\"mousemove\", updateMouse, false);\n  } else {\n    inst.style.display = \"flex\";\n    document.removeEventListener(\"mousemove\", updateMouse, false);\n  }\n}\n\nlet up = false;\nlet right = false;\nlet down = false;\nlet left = false;\n\ndocument.addEventListener('keydown', press);\nfunction press(e){\n  if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */){\n    up = true;\n  }\n  if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */){\n    right = true;\n  }\n  if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */){\n    down = true;\n  }\n  if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */){\n    left = true;\n  }\n}\n\ndocument.addEventListener('keyup', release);\nfunction release(e){\n  if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */){\n    up = false\n  }\n  if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */){\n    right = false\n  }\n  if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */){\n    down = false\n  }\n  if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */){\n    left = false\n  }\n}\n\nconst prevMouseLocation = {\n  x: NaN, y: NaN,\n}\n\nconst clamp = function(val, min, max) {\n  return Math.min(Math.max(val, min), max);\n}\nconst degToRad = function(deg) {\n  return Math.PI * deg / 180;\n}\n\nconst clampPosition0 = function(prev, position) {\n  // Long Corridor\n  if (position[0] <= 90 || (prev[0] <= 90 && prev[2] > -1010)) {\n    position[0] = clamp(position[0], -90, 90);\n    position[2] = clamp(position[2], -1190, -10);\n  }\n  // Top\n  else if (prev[2] <= -1010) {\n    position[0] = clamp(position[0], -90, 490);\n    if (prev[0] > 90 && prev[0] < 310) {\n      position[2] = clamp(position[2], -1190, -1010);\n    } else {\n      position[2] = clamp(position[2], -1190, -10);\n    }\n  }\n  // Right\n  else if (position[0] >= 310 || (prev[0] >= 310 && prev[2] > -1010)) {\n    position[0] = clamp(position[0], 310, 490);\n    position[2] = clamp(position[2], -1190, -610);\n  }\n\n  return position;\n}\n\nconst clampPosition1 = function(prev, position) {\n  position[0] = clamp(position[0], 610, 1190);\n  position[2] = clamp(position[2], -1190, -610);\n\n  if (prev[0] <= 790 && (prev[2] <= -790 && prev[2] > -1010)) {\n    position[0] = clamp(position[0], 610, 790);\n  }\n  else if (prev[2] <= -1010) {\n    if (prev[0] > 790 && prev[0] < 1010) {\n      position[2] = clamp(position[2], -1190, -1010);\n    } else {\n      position[2] = clamp(position[2], -1190, -610);\n    }\n  }\n  else if (prev[0] >= 1010  && (prev[2] <= -790 && prev[2] > -1010)) {\n    position[0] = clamp(position[0], 1010, 1190);\n  }\n  else {\n    if (prev[0] > 790 && prev[0] < 1010) {\n      position[2] = clamp(position[2], -790, -610);\n    } else {\n      position[2] = clamp(position[2], -1190, -610);\n    }\n  }\n\n  return position;\n}\n\nconst clampPosition2 = function(prev, position) {\n  position[0] = clamp(position[0], 1310, 1890);\n  position[2] = clamp(position[2], -1790, -610);\n\n  // Long Corridor\n  if (position[0] <= 1490 || (prev[0] <= 1490 && prev[2] < -790)) {\n    position[0] = clamp(position[0], 1310, 1490);\n    position[2] = clamp(position[2], -1790, -610);\n  }\n  // Bottom\n  else if (prev[2] >= -790) {\n    if (prev[0] > 1490 && prev[0] < 1710) {\n      position[2] = clamp(position[2], -790, -610);\n    } else {\n      position[2] = clamp(position[2], -1790, -610);\n    }\n  }\n  // Right\n  else if (position[0] >= 310 || (prev[0] >= 310 && prev[2] > -1010)) {\n    position[0] = clamp(position[0], 310, 490);\n    position[2] = clamp(position[2], -1190, -610);\n  }\n\n  return position;\n}\n\nconst checkZone = function(prevPos) {\n  const newPos = camera.position;\n  // Zone 0 to 1\n  if (zone === 0 && newPos[0] > 310 && prevPos[2] <= -980 && newPos[2] > -980) {\n    camera.translate(700, 0, 0);\n    zone = 1;\n    rotate = 0;\n  } else if (zone === 1 && newPos[0] > 1010) {\n    if (prevPos[2] <= -980 && newPos[2] > -980) {\n      rotate += 1;\n    } else if (prevPos[2] > -980 && newPos[2] <= -980) {\n      rotate -= 1;\n    }\n    if (rotate < 0) {\n      camera.translate(-700, 0, 0);\n      zone = 0;\n    }\n  } else if (zone === 1 && newPos[2] >= -790) {\n    if (prevPos[0] > 980 && newPos[0] <= 980) {\n      if (rotate > 5) {\n        camera.translate(700, 0, 0);\n        zone = 2;\n      }\n    }\n  } else if (zone === 2 && newPos[2] >= -790) {\n    if (prevPos[0] < 1680 && newPos[0] >= 1680) {\n      camera.translate(-700, 0, 0);\n      zone = 1;\n    }\n  }\n}\n\nconst updateMouse = function(e) {\n  const diffX = e.movementX;\n  const diffY = e.movementY;\n\n  camera.rotate(-diffY / 5, diffX / 5);\n  camera.rotation = [clamp(camera.rotation[0], -85, 85), camera.rotation[1], camera.rotation[2]];\n  camera.update(0);\n\n  prevMouseLocation.x = e.screenX;\n  prevMouseLocation.y = e.screenY;\n}\nconst speed = 5;\nconst keyLoop = function() {\n  const prevPos = camera.position.concat();\n  const speedVal = speed / Math.cos(degToRad(camera.rotation[0]));\n\n  if (up){\n    camera.translateLocal(0, 0, -speedVal);\n  }\n  if (right){\n    camera.translateLocal(speed, 0, 0);\n  }\n  if (down){\n    camera.translateLocal(0, 0, speedVal);\n  }\n  if (left){\n    camera.translateLocal(-speed, 0, 0);\n  }\n  var newPos = [camera.position[0], 0, camera.position[2]];\n  camera.position = zone === 0\n    ? clampPosition0(prevPos, newPos)\n    : zone === 1\n      ? clampPosition1(prevPos, newPos)\n      : clampPosition2(prevPos, newPos);\n  checkZone(prevPos);\n  camera.update(0);\n  requestAnimationFrame(keyLoop);\n}\nkeyLoop();\n"
  },
  {
    "path": "demo/js/index.js",
    "content": "const windowWidth = document.body.offsetWidth;\n\nconst cardButton = document.querySelector(\"#card-button\");\nconst codeButton = document.querySelector(\"#code-button\");\nconst heroButton = document.querySelector(\"#hero-button\");\nconst moreButton = document.querySelector(\"#more-button\");\nconst buttons = [\n  cardButton, codeButton, heroButton, moreButton,\n];\n\nconst card = document.querySelector(\"#card\");\nconst code = document.querySelector(\"#code\");\nconst hero = document.querySelector(\"#hero\");\nconst more = document.querySelector(\"#more\");\n\nconst restoreButtons = () => {\n  buttons.forEach(button => button.classList.add(\"is-outlined\"));\n}\n\nconst flicking = new eg.Flicking(\"#demo-flicking\", {\n  collectStatistics: false,\n  adaptive: true,\n  zIndex: \"\",\n  gap: 50,\n  overflow: true\n});\n\nconst camera = new CSSCamera(\"#space\", {\n  perspective: windowWidth / 2,\n});\n\ncamera.focus(hero);\ncamera.update(2000);\n\ncardButton.onclick = () => {\n  camera.focus(card);\n  camera.update(2000);\n  restoreButtons();\n  cardButton.classList.remove(\"is-outlined\");\n}\ncodeButton.onclick = () => {\n  camera.focus(code);\n  camera.update(2000);\n  restoreButtons();\n  codeButton.classList.remove(\"is-outlined\");\n}\nheroButton.onclick = () => {\n  camera.focus(hero);\n  camera.update(2000);\n  restoreButtons();\n  heroButton.classList.remove(\"is-outlined\");\n}\nmoreButton.onclick = () => {\n  camera.focus(more);\n  camera.update(2000);\n  restoreButtons();\n  moreButton.classList.remove(\"is-outlined\");\n}\n"
  },
  {
    "path": "demo/js/ortho.js",
    "content": "const windowWidth = window.innerWidth;\nconst windowHeight = window.innerHeight;\nconst center = document.querySelector(\"#center\");\n\nconst camera = new CSSCamera(\"#space\");\ncamera.viewportEl.style.backgroundColor = \"white\";\ncamera.focus(center);\ncamera.update(0).then(async () => {\n  await camera.update(1000);\n\n  camera.rotation = [55, 0, 0];\n\n  await camera.update(1000);\n\n  camera.rotation = [55, 0, -45];\n  camera.scale = [2, 2, 2];\n  camera.viewportEl.style.backgroundColor = \"black\";\n  await camera.update(2000, {\n    property: \"transform, background-color\",\n    timingFunction: \"ease-out, linear\",\n    delay: \"0ms, 0ms\"\n  });\n\n  // Controls after init\n  const Axes = eg.Axes;\n  const PanInput = Axes.PanInput;\n  const axes = new Axes({\n    x: { range: [-400, 400] },\n    y: { range: [-150, 250] }\n  }, { deceleration: 0.004 }, { x: 0, y: 0 });\n  const panInput = new PanInput(\".cc-viewport\", {\n    scale: [0.3, 0.3],\n  });\n\n  axes.on({\n    \"change\": evt => {\n      camera.translateLocal(-evt.delta.x, -evt.delta.y);\n      camera.update(0);\n    }\n  });\n\n  axes.connect([\"x\", \"y\"], panInput);\n});\n"
  },
  {
    "path": "demo/ortho.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>css-camera</title>\n    <meta charsset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">\n    <meta property=\"og:title\" content=\"css-camera\" />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:url\" content=\"https://woodneck.github.io/css-camera/\" />\n    <script defer src=\"https://use.fontawesome.com/releases/v5.3.1/js/all.js\"></script>\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/ortho.css\">\n    <script src=\"./release/latest/lib/css-camera.pkgd.js\"></script>\n    <script src=\"https://naver.github.io/egjs-axes/release/latest/dist/axes.pkgd.js\"></script>\n  </head>\n  <body>\n    <div id=\"space\">\n      <div class=\"tile grass road-vertical\"></div>\n      <div class=\"tile grass\"><div class=\"fence\"></div></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass grassi height-1\"></div>\n      <div class=\"tile grass grassh height-2\"></div>\n      <div class=\"tile grass height-3\"><div class=\"bush\"></div></div>\n      <div class=\"tile grass grassg height-3\"></div>\n      <div class=\"tile grass height-4\"></div>\n      <div class=\"tile grass grassh height-4\"></div>\n      <div class=\"tile grass height-5\"><div class=\"flower\"></div></div>\n\n      <div class=\"tile grass road-vertical\"></div>\n      <div class=\"tile grass grassc\"></div>\n      <div class=\"tile grass\"><div class=\"rock\"></div></div>\n      <div class=\"tile grass grassa\"></div>\n      <div class=\"tile grass grassb height-1\"></div>\n      <div class=\"tile grass grassh height-2\"></div>\n      <div class=\"tile grass grassd height-2\"></div>\n      <div class=\"tile grass patch height-3\"></div>\n      <div class=\"tile grass grasse height-4\"></div>\n      <div class=\"tile grass height-5\"></div>\n\n      <div class=\"tile grass road-vertical\"></div>\n      <div class=\"tile grass\"><div class=\"barrel\"></div></div>\n      <div class=\"tile grass grassb\"></div>\n      <div class=\"tile grass patch\"></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass height-1\"></div>\n      <div class=\"tile grass height-1\"></div>\n      <div class=\"tile grass height-2\"></div>\n      <div class=\"tile grass height-3\"><div class=\"rock\"></div></div>\n      <div class=\"tile grass grassg height-4\"></div>\n\n      <div class=\"tile grass road-bottom\"></div>\n      <div class=\"tile grass\"><div class=\"sign\"></div></div>\n      <div class=\"tile grass\"><div class=\"flower\"></div></div>\n      <div class=\"tile grass grassi\"></div>\n      <div class=\"tile grass\"><div class=\"tree\"></div></div>\n      <div class=\"tile grass\" id=\"center\"></div>\n      <div class=\"tile grass grassc height-1\"></div>\n      <div class=\"tile grass grassi height-1\"></div>\n      <div class=\"tile grass grassd height-2\"></div>\n      <div class=\"tile grass height-3\"></div>\n\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass river-top-left\"></div>\n      <div class=\"tile grass river-top\"></div>\n      <div class=\"tile grass river-top\"></div>\n      <div class=\"tile grass river-top-right\"></div>\n      <div class=\"tile grass\"><div class=\"fence\"></div></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass grassh\"></div>\n      <div class=\"tile grass height-1\"></div>\n      <div class=\"tile grass grassh height-2\"></div>\n\n      <div class=\"tile grass grassh\"></div>\n      <div class=\"tile grass river-bottom-left\"></div>\n      <div class=\"tile grass river-bottom\"></div>\n      <div class=\"tile grass river-bottom\"></div>\n      <div class=\"tile grass river-bottom-right\"></div>\n      <div class=\"tile grass grassf\"></div>\n      <div class=\"tile grass grassg\"></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass\"><div class=\"bush\"></div></div>\n      <div class=\"tile grass height-1\"></div>\n\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass grassb\"></div>\n      <div class=\"tile grass\"><div class=\"chopped\"></div></div>\n      <div class=\"tile grass grassc\"></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass grassa\"></div>\n      <div class=\"tile grass\"></div>\n      <div class=\"tile grass road-top\"></div>\n      <div class=\"tile grass grassg\"></div>\n      <div class=\"tile grass\"><div class=\"flower\"></div></div>\n    </div>\n    <div id=\"info\">All pixelart assets are from <a href=\"https://guttykreum.itch.io/field-of-green\">https://guttykreum.itch.io/field-of-green</a></div>\n    <div id=\"info2\"><a href=\"https://github.com/WoodNeck/css-camera/blob/master/demo/js/ortho.js\"><i class=\"fab fa-github\"></i> Source</a></div>\n    <script src=\"js/ortho.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "jsdoc.json",
    "content": "{\n  \"tags\": {\n    \"allowUnknownTags\" : false,\n    \"dictionaries\": [\"jsdoc\", \"closure\"]\n  },\n  \"source\": {\n      \"include\": [\"src\", \"README.md\"],\n      \"includePattern\": \".+\\\\.(j|t)s(doc|x)?$\",\n      \"excludePattern\": \"(^|\\\\/|\\\\\\\\)_\"\n  },\n  \"opts\": {\n      \"template\": \"node_modules/docdash\",\n      \"destination\": \"./docs/\",\n      \"ignores\": [],\n      \"expendsItemMembers\": true,\n      \"recurse\": true\n  },\n  \"plugins\": [\n    \"plugins/markdown\"\n  ],\n  \"docdash\": {\n    \"static\": true,\n    \"cleverLinks\": true,\n    \"monospaceLinks\": true,\n    \"typedefs\": true,\n    \"private\": true,\n    \"search\": true,\n    \"openGraph\": {\n      \"title\": \"css-camera\",\n      \"type\": \"website\",\n      \"site_name\": \"css-camera\",\n      \"image\": \"\",\n      \"url\": \"https://woodneck.github.io/css-camera/\"\n    },\n    \"meta\": {\n      \"title\": \"css-camera\",\n      \"description\": \"Add a depth to your web page with CSS3 3D transform.\",\n      \"keyword\": \"css3, camera, graphics, 3d\"\n    },\n    \"menu\":{\n      \"Github\":{\n          \"href\":\"https://github.com/WoodNeck/css-camera\",\n          \"target\":\"_blank\"\n      }\n    }\n  },\n  \"markdown\": {\n    \"parser\": \"gfm\",\n    \"hardwrap\": true\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"css-camera\",\n  \"version\": \"1.0.1-snapshot\",\n  \"description\": \"Add a depth to your web page with CSS3 3D transform.\",\n  \"main\": \"lib/css-camera.js\",\n  \"module\": \"lib/css-camera.esm.js\",\n  \"types\": \"lib/declaration/index.d.ts\",\n  \"scripts\": {\n    \"build\": \"rm -rf ./lib && rollup -c && npm run declaration\",\n    \"build:windows\": \"rd /s /q ./lib || rollup -c && npm run declaration:windows\",\n    \"declaration\": \"rm -rf ./lib/declaration && tsc -p tsconfig.json\",\n    \"declaration:windows\": \"rd /s /q ./lib || tsc -p tsconfig.json\",\n    \"demo:build\": \"npm run build && cpx 'lib/**/*' demo/release/latest/lib --clean\",\n    \"demo:prebuild-version\": \"cpx 'lib/**/*' demo/release/$npm_package_version/lib --clean && cpx 'docs/**/*' demo/release/$npm_package_version/docs --clean\",\n    \"demo:prebuild-latest\": \"cpx 'lib/**/*' demo/release/latest/lib --clean && cpx 'docs/**/*' demo/release/latest/docs --clean\",\n    \"demo:deploy\": \"npm run build && npm run doc && npm run demo:prebuild-version && npm run demo:prebuild-latest && gh-pages -d demo/\",\n    \"doc\": \"rm -rf ./docs && jsdoc -c jsdoc.json\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/WoodNeck/css-camera.git\"\n  },\n  \"author\": \"WoodNeck\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/WoodNeck/css-camera/issues\"\n  },\n  \"homepage\": \"https://github.com/WoodNeck/css-camera#readme\",\n  \"devDependencies\": {\n    \"@daybrush/jsdoc\": \"^0.3.7\",\n    \"@egjs/build-helper\": \"0.0.5\",\n    \"@types/gl-matrix\": \"^2.4.5\",\n    \"cpx\": \"^1.5.0\",\n    \"docdash\": \"^1.1.1\",\n    \"gh-pages\": \"2.0.1\",\n    \"rollup\": \"^1.10.1\",\n    \"rollup-plugin-node-resolve\": \"^4.2.3\",\n    \"rollup-plugin-prototype-minify\": \"^1.0.5\",\n    \"rollup-plugin-replace\": \"^2.2.0\",\n    \"rollup-plugin-typescript\": \"^1.0.1\",\n    \"rollup-plugin-uglify\": \"^6.0.2\",\n    \"tslint\": \"^5.15.0\",\n    \"tslint-consistent-codestyle\": \"^1.15.1\",\n    \"tslint-eslint-rules\": \"^5.4.0\",\n    \"typescript\": \"^3.4.3\"\n  },\n  \"dependencies\": {\n    \"gl-matrix\": \"^3.0.0\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "const buildHelper = require(\"@egjs/build-helper\");\n\nconst name = \"CSSCamera\";\nconst external = {\n\t\"gl-matrix\": \"gl-matrix\",\n}\nexport default buildHelper([\n  {\n    name,\n    input: \"./src/index.umd.ts\",\n    output: \"./lib/css-camera.js\",\n    format: \"umd\",\n    external,\n  },\n  {\n    name,\n    input: \"./src/index.umd.ts\",\n    output: \"./lib/css-camera.min.js\",\n    format: \"umd\",\n    uglify: true,\n    external,\n  },\n  {\n    name,\n    input: \"./src/index.umd.ts\",\n    output: \"./lib/css-camera.pkgd.js\",\n    format: \"umd\",\n    resolve: true,\n  },\n  {\n    name,\n    input: \"./src/index.umd.ts\",\n    output: \"./lib/css-camera.pkgd.min.js\",\n    format: \"umd\",\n    resolve: true,\n    uglify: true,\n  },\n  {\n    input: \"./src/index.ts\",\n    output: \"./lib/css-camera.esm.js\",\n    format: \"esm\",\n    external,\n    exports: \"named\",\n  },\n]);\n"
  },
  {
    "path": "src/CSSCamera.ts",
    "content": "import { mat4, vec3, quat } from 'gl-matrix';\nimport { getElement, applyCSS, getTransformMatrix, findIndex, getOffsetFromParent, getRotateOffset, assign } from './utils/helper';\nimport { quatToEuler } from './utils/math';\nimport * as DEFAULT from './constants/default';\nimport { Offset, UpdateOption, ValueOf, Options } from './types';\n\nclass CSSCamera {\n  private _element: HTMLElement;\n  private _viewportEl: HTMLElement;\n  private _cameraEl: HTMLElement;\n  private _worldEl: HTMLElement;\n\n  private _position: vec3;\n  private _scale: vec3;\n  private _rotation: vec3;\n  private _perspective: number;\n  private _rotateOffset: number;\n  private _updateTimer: number;\n\n  /**\n   * Current version of CSSCamera.\n   * @example\n   * console.log(CSSCamera.VERSION); // ex) 1.0.0\n   * @type {string}\n   */\n  static get VERSION() { return '#__VERSION__#'; }\n\n  /**\n   * The element provided in the constructor.\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.element === el); // true\n   * @type {HTMLElement}\n   */\n  public get element() { return this._element; }\n\n  /**\n   * The reference of viewport DOM element.\n   * @type {HTMLElement}\n   */\n  public get viewportEl() { return this._viewportEl; }\n\n  /**\n   * The reference of camera DOM element.\n   * @type {HTMLElement}\n   */\n  public get cameraEl() { return this._cameraEl; }\n\n  /**\n   * The reference of world DOM element.\n   * @type {HTMLElement}\n   */\n  public get worldEl() { return this._worldEl; }\n\n  /**\n   * The current position as number array([x, y, z]).\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.position); // [0, 0, 0];\n   * camera.position = [0, 0, 300];\n   * console.log(camera.position); // [0, 0, 300];\n   * @type {number[]}\n   */\n  public get position() { return [...this._position]; }\n\n  /**\n   * The current scale as number array([x, y, z]).\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.scale); // [1, 1, 1];\n   * camera.scale = [5, 1, 1];\n   * console.log(camera.scale); // [5, 1, 1];\n   * @type {number[]}\n   */\n  public get scale() { return [...this._scale]; }\n\n  /**\n   * The current Euler rotation angles in degree as number array([x, y, z]).\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.rotation); // [0, 0, 0];\n   * camera.rotation = [90, 0, 0];\n   * console.log(camera.rotation); // [90, 0, 0];\n   * @type {number[]}\n   */\n  public get rotation() { return [...this._rotation]; }\n\n  /**\n   * The current quaternion rotation as number array([x, y, z, w]).\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.quaternion); // [0, 0, 0, 1];\n   * camera.rotation = [90, 0, 0];\n   * console.log(camera.quaternion); // [0.7071067690849304, 0, 0, 0.7071067690849304];\n   * camera.quaternion = [0, 0, 0, 1];\n   * console.log(camera.rotation); // [0, -0, 0];\n   * @type {number[]}\n   */\n  public get quaternion() {\n    const r = this._rotation;\n    const quaternion = quat.fromEuler(quat.create(), r[0], r[1], r[2]);\n\n    return [...quaternion];\n  }\n\n  /**\n   * The current perspective value that will be applied to viewport element.\n   * @example\n   * const camera = new CSSCamera(el);\n   * camera.perspective = 300;\n   * console.log(camera.perspective); // 300\n   * @type {number}\n   */\n  public get perspective() { return this._perspective; }\n\n  /**\n   * The current rotate offset value that will be applied to camera element.\n   * The camera will be as far away from the focal point as this value.\n   * |![rot0](https://woodneck.github.io/css-camera/asset/rot0.gif)|![rot150](https://woodneck.github.io/css-camera/asset/rot150.gif)|\n   * |:---:|:---:|\n   * @example\n   * const camera = new CSSCamera(el);\n   * camera.perspective = 300;\n   * console.log(camera.cameraCSS); // scale3d(1, 1, 1) translateZ(300px) rotateX(0deg) rotateY(0deg) rotateZ(0deg);\n   * camera.rotateOffset = 100;\n   * console.log(camera.cameraCSS); // scale3d(1, 1, 1) translateZ(400px) rotateX(0deg) rotateY(0deg) rotateZ(0deg);\n   * @type {number}\n   */\n  public get rotateOffset() { return this._rotateOffset; }\n\n  /**\n   * CSS string can be applied to camera element based on current transform.\n   * @example\n   * const camera = new CSSCamera(el);\n   * camera.perspective = 300;\n   * console.log(camera.cameraCSS); // scale3d(1, 1, 1) translateZ(300px) rotateX(0deg) rotateY(0deg) rotateZ(0deg);\n   * @type {string}\n   */\n  public get cameraCSS() {\n    const perspective = this._perspective;\n    const rotateOffset = this._rotateOffset;\n    const rotation = this._rotation;\n    const scale = this._scale;\n\n    // Rotate in order of Z - Y - X\n    // tslint:disable-next-line: max-line-length\n    return `scale3d(${scale[0]}, ${scale[1]}, ${scale[2]}) translateZ(${perspective - rotateOffset}px) rotateX(${rotation[0]}deg) rotateY(${rotation[1]}deg) rotateZ(${rotation[2]}deg)`;\n  }\n\n  /**\n   * CSS string can be applied to world element based on current transform.\n   * ```\n   * const camera = new CSSCamera(el);\n   * console.log(camera.worldCSS); // \"translate3d(0px, 0px, 0px)\";\n   * camera.translate(0, 0, 300);\n   * console.log(camera.worldCSS); // \"translate3d(0px, 0px, -300px)\";\n   * ```\n   * @type {string}\n   */\n  public get worldCSS() {\n    const position = this._position;\n\n    return `translate3d(${-position[0]}px, ${-position[1]}px, ${-position[2]}px)`;\n  }\n\n  public set position(val: number[]) { this._position = vec3.fromValues(val[0], val[1], val[2]); }\n  public set scale(val: number[]) { this._scale = vec3.fromValues(val[0], val[1], val[2]); }\n  public set rotation(val: number[]) { this._rotation = vec3.fromValues(val[0], val[1], val[2]); }\n  public set quaternion(val: number[]) { this._rotation = quatToEuler(quat.fromValues(val[0], val[1], val[2], val[3])); }\n  public set perspective(val: number) { this._perspective = val; }\n  public set rotateOffset(val: number) { this._rotateOffset = val; }\n\n  /**\n   * Create new CSSCamera with given element / selector.\n   * @param - The element to apply camera. Can be HTMLElement or CSS selector.\n   * @param {Partial<Options>} [options] Camera options\n   * @param {number[]} [options.position=[0, 0, 0]] Initial position of the camera.\n   * @param {number[]} [options.scale=[1, 1, 1]] Initial scale of the camera.\n   * @param {number[]} [options.rotation=[0, 0, 0]] Initial Euler rotation angles(x, y, z) of the camera in degree.\n   * @param {number} [options.perspective=0] Initial perspective of the camera.\n   * @param {number} [options.rotateOffset=0] Initial rotate offset of the camera.\n   * @example\n   * const camera = new CSSCamera(\"#el\", {\n   *   position: [0, 0, 150], // Initial pos(x, y, z)\n   *   rotation: [90, 0, 0],  // Initial rotation(x, y, z, in degree)\n   *   perspective: 300       // CSS \"perspective\" value to apply\n   * });\n   */\n  constructor(el: string | HTMLElement, options: Partial<Options> = {}) {\n    this._element = getElement(el);\n\n    const op = assign(assign({}, DEFAULT.OPTIONS), options) as Options;\n\n    this._position = vec3.fromValues(op.position[0], op.position[1], op.position[2]);\n    this._scale = vec3.fromValues(op.scale[0], op.scale[1], op.scale[2]);\n    this._rotation = vec3.fromValues(op.rotation[0], op.rotation[1], op.rotation[2]);\n    this._perspective = op.perspective;\n    this._rotateOffset = op.rotateOffset;\n    this._updateTimer = -1;\n\n    const element = this._element;\n    const viewport = document.createElement('div');\n    const camera = viewport.cloneNode() as HTMLElement;\n    const world = viewport.cloneNode() as HTMLElement;\n\n    viewport.className = DEFAULT.CLASS.VIEWPORT;\n    camera.className = DEFAULT.CLASS.CAMERA;\n    world.className = DEFAULT.CLASS.WORLD;\n\n    applyCSS(viewport, DEFAULT.STYLE.VIEWPORT);\n    applyCSS(camera, DEFAULT.STYLE.CAMERA);\n    applyCSS(world, DEFAULT.STYLE.WORLD);\n\n    camera.appendChild(world);\n    viewport.appendChild(camera);\n\n    this._viewportEl = viewport;\n    this._cameraEl = camera;\n    this._worldEl = world;\n\n    // EL's PARENT -> VIEWPORT -> CAMERA -> WORLD -> EL\n    element.parentElement!.insertBefore(viewport, element);\n    world.appendChild(element);\n\n    this.update(0);\n  }\n\n  /**\n   * Focus a camera to given element.\n   * After focus, element will be in front of camera with no rotation applied.\n   * Also, it will have original width / height if neither [scale](#scale) nor [perspectiveOffset](#perspectiveOffset) is applied.\n   * This method won't work if any of element's parent except camera element has scale applied.\n   * @param - The element to focus. Can be HTMLElement or CSS selector.\n   * @return {CSSCamera} The instance itself\n   */\n  public focus(el: string | HTMLElement): this {\n    const element = getElement(el);\n    const focusMatrix = this._getFocusMatrix(element);\n\n    const rotation = quat.create();\n    const translation = vec3.create();\n    mat4.getRotation(rotation, focusMatrix);\n    mat4.getTranslation(translation, focusMatrix);\n\n    const eulerAngle = quatToEuler(rotation);\n\n    vec3.negate(eulerAngle, eulerAngle);\n\n    this._rotation = eulerAngle;\n    this._position = translation;\n    return this;\n  }\n\n  /**\n   * Translate a camera in its local coordinate space.\n   * For example, `camera.translateLocal(0, 0, -300)` will always move camera to direction where it's seeing.\n   * @param - Amount of horizontal translation, in px.\n   * @param - Amount of vertical translation, in px.\n   * @param - Amount of translation in view direction, in px.\n   * @return {CSSCamera} The instance itself\n   */\n  public translateLocal(x: number = 0, y: number = 0, z: number = 0): this {\n    const position = this._position;\n    const rotation = this._rotation;\n\n    const transVec = vec3.fromValues(x, y, z);\n    const rotQuat = quat.create();\n    quat.fromEuler(rotQuat, -rotation[0], -rotation[1], -rotation[2]);\n    vec3.transformQuat(transVec, transVec, rotQuat);\n\n    vec3.add(position, position, transVec);\n    return this;\n  }\n\n  /**\n   * Translate a camera in world(absolute) coordinate space.\n   * @param - Amount of translation in x axis, in px.\n   * @param - Amount of translation in y axis, in px.\n   * @param - Amount of translation in z axis, in px.\n   * @return {CSSCamera} The instance itself\n   */\n  public translate(x: number = 0, y: number = 0, z: number = 0): this {\n    vec3.add(this._position, this._position, vec3.fromValues(x, y, z));\n\n    return this;\n  }\n\n  /**\n   * Rotate a camera in world(absolute) coordinate space.\n   * @param - Amount of rotation in x axis, in degree.\n   * @param - Amount of rotation in y axis, in degree.\n   * @param - Amount of rotation in z axis, in degree.\n   * @return {CSSCamera} The instance itself\n   */\n  public rotate(x: number = 0, y: number = 0, z: number = 0): this {\n    vec3.add(this._rotation, this._rotation, vec3.fromValues(x, y, z));\n\n    return this;\n  }\n\n  /**\n   * Updates a camera CSS with given duration.\n   * Every other camera transforming properties / methods will be batched until this method is called.\n   * @example\n   * const camera = new CSSCamera(el);\n   * console.log(camera.cameraEl.style.transform); // ''\n   *\n   * camera.perspective = 300;\n   * camera.translate(0, 0, 300);\n   * camera.rotate(0, 90, 0);\n   * console.log(camera.cameraEl.style.transform); // '', Not changed!\n   *\n   * await camera.update(1000); // Camera style is updated.\n   * console.log(camera.cameraEl.style.transform); // scale3d(1, 1, 1) translateZ(300px) rotateX(0deg) rotateY(90deg) rotateZ(0deg)\n   *\n   * // When if you want to apply multiple properties\n   * camera.update(1000, {\n   *   property: \"transform, background-color\",\n   *   timingFunction: \"ease-out, ease-out\", // As same with CSS, you should assign values to each property\n   *   delay: \"0ms, 100ms\"\n   * });\n   * @param - Transition duration in ms.\n   * @param {Partial<UpdateOption>} [options] Transition options.\n   * @param {string} [options.property=\"transform\"] CSS [transition-property](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-property) to apply.\n   * @param {string} [options.timingFunction=\"ease-out\"] CSS [transition-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function) to apply.\n   * @param {string} [options.delay=\"0ms\"] CSS [transition-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-delay) to apply.\n   * @return {Promise<CSSCamera>} A promise resolving instance itself\n   */\n  public async update(duration: number = 0, options: Partial<UpdateOption> = {}): Promise<this> {\n    applyCSS(this._viewportEl, { perspective: `${this.perspective}px` });\n    applyCSS(this._cameraEl, { transform: this.cameraCSS });\n    applyCSS(this._worldEl, { transform: this.worldCSS });\n\n    const updateOptions = assign(assign({}, DEFAULT.UPDATE_OPTIONS), options) as UpdateOption;\n\n    if (duration > 0) {\n      if (this._updateTimer > 0) {\n        window.clearTimeout(this._updateTimer);\n      }\n\n      const transitionDuration = `${duration}ms`;\n      const updateOption = Object.keys(updateOptions).reduce((option: {[key: string]: ValueOf<UpdateOption>}, key) => {\n        option[`transition${key.charAt(0).toUpperCase() + key.slice(1)}`] = updateOptions[key as keyof UpdateOption]!;\n        return option;\n      }, {});\n\n      const finalOption = {\n        transitionDuration,\n        ...updateOption,\n      };\n\n      [this._viewportEl, this._cameraEl, this._worldEl].forEach(el => {\n        applyCSS(el, finalOption);\n      });\n    }\n\n    return new Promise(resolve => {\n      // Make sure to use requestAnimationFrame even if duration is 0\n      // To make sure DOM is updated, for successive update() calls.\n      if (duration > 0) {\n        this._updateTimer = window.setTimeout(() => {\n          // Reset transition values\n          [this._viewportEl, this._cameraEl, this._worldEl].forEach(el => {\n            applyCSS(el, { transition: '' });\n          });\n          this._updateTimer = -1;\n          resolve();\n        }, duration);\n      } else {\n        requestAnimationFrame(() => {\n          resolve();\n        });\n      }\n    });\n  }\n\n  private _getFocusMatrix(element: HTMLElement): mat4 {\n    const elements: HTMLElement[] = [];\n    while (element) {\n      elements.push(element);\n      if (element === this._element) break;\n      element = element.parentElement!;\n    }\n\n    // Order by shallow to deep\n    elements.reverse();\n\n    const elStyles = elements.map(el => window.getComputedStyle(el));\n\n    // Find first element that transform-style is not preserve-3d\n    // As all childs of that element is affected by its matrix\n    const firstFlatIndex = findIndex(elStyles, style => style.transformStyle !== 'preserve-3d');\n    if (firstFlatIndex > 0) { // el doesn't have to be preserve-3d'ed\n      elStyles.splice(firstFlatIndex + 1);\n    }\n\n    let parentOffset: Offset = {\n      left: 0,\n      top: 0,\n      width: this.viewportEl.offsetWidth,\n      height: this.viewportEl.offsetHeight,\n    };\n\n    // Accumulated rotation\n    const accRotation = quat.identity(quat.create());\n    // Assume center of screen as (0, 0, 0)\n    const centerPos = vec3.fromValues(0, 0, 0);\n\n    elStyles.forEach((style, idx) => {\n      const el = elements[idx];\n      const currentOffset = {\n        left: el.offsetLeft,\n        top: el.offsetTop,\n        width: el.offsetWidth,\n        height: el.offsetHeight,\n      };\n      const transformMat = getTransformMatrix(style);\n      const offsetFromParent = getOffsetFromParent(currentOffset, parentOffset);\n      vec3.transformQuat(offsetFromParent, offsetFromParent, accRotation);\n\n      vec3.add(centerPos, centerPos, offsetFromParent);\n\n      const rotateOffset = getRotateOffset(style, currentOffset);\n      vec3.transformQuat(rotateOffset, rotateOffset, accRotation);\n\n      const transformOrigin = vec3.clone(centerPos);\n      vec3.add(transformOrigin, transformOrigin, rotateOffset);\n\n      const centerFromOrigin = vec3.create();\n      vec3.sub(centerFromOrigin, centerPos, transformOrigin);\n\n      const invAccRotation = quat.invert(quat.create(), accRotation);\n      vec3.transformQuat(centerFromOrigin, centerFromOrigin, invAccRotation);\n      vec3.transformMat4(centerFromOrigin, centerFromOrigin, transformMat);\n      vec3.transformQuat(centerFromOrigin, centerFromOrigin, accRotation);\n\n      const newCenterPos = vec3.add(vec3.create(), transformOrigin, centerFromOrigin);\n      const rotation = mat4.getRotation(quat.create(), transformMat);\n\n      vec3.copy(centerPos, newCenterPos);\n      quat.mul(accRotation, accRotation, rotation);\n      parentOffset = currentOffset;\n    });\n\n    const perspective = vec3.fromValues(0, 0, this.perspective);\n    vec3.transformQuat(perspective, perspective, accRotation);\n    vec3.add(centerPos, centerPos, perspective);\n\n    const matrix = mat4.create();\n    mat4.fromRotationTranslation(matrix, accRotation, centerPos);\n\n    return matrix;\n  }\n}\n\nexport default CSSCamera;\n"
  },
  {
    "path": "src/constants/default.ts",
    "content": "export const STYLE = {\n  VIEWPORT: {\n    width: '100%',\n    height: '100%',\n    'transform-style': 'preserve-3d',\n    overflow: 'hidden',\n  },\n  CAMERA: {\n    width: '100%',\n    height: '100%',\n    'transform-style': 'preserve-3d',\n    'will-change': 'transform',\n  },\n  WORLD: {\n    width: '100%',\n    height: '100%',\n    'transform-style': 'preserve-3d',\n    'will-change': 'transform',\n  },\n};\n\nexport const CLASS = {\n  VIEWPORT: 'cc-viewport',\n  CAMERA: 'cc-camera',\n  WORLD: 'cc-world',\n};\n\nexport const OPTIONS = {\n  position: [0, 0, 0],\n  scale: [1, 1, 1],\n  rotation: [0, 0, 0],\n  perspective: 0,\n  rotateOffset: 0,\n};\n\nexport const UPDATE_OPTIONS = {\n  property: 'transform',\n  timingFunction: 'ease-out',\n  delay: '0ms',\n};\n"
  },
  {
    "path": "src/constants/error.ts",
    "content": "export const ELEMENT_NOT_EXIST = (selector: string) => `Element with selector \"${selector}\" doesn't exist.`;\nexport const MUST_STRING_OR_ELEMENT = (received: any) => `Element should be provided in string or HTMLElement. Received: ${received}`;\n"
  },
  {
    "path": "src/index.ts",
    "content": "import CSSCamera from './CSSCamera';\n\nexport * from './types';\nexport default CSSCamera;\n"
  },
  {
    "path": "src/index.umd.ts",
    "content": "import CSSCamera from './CSSCamera';\n\nexport default CSSCamera;\n"
  },
  {
    "path": "src/types.ts",
    "content": "export type ValueOf<T> = T[keyof T];\n\nexport type Matrix4x4 = [\n  number, number, number, number,\n  number, number, number, number,\n  number, number, number, number,\n  number, number, number, number,\n];\n\n/**\n * @typedef\n * @property - Initial position of the camera.\n * @property - Initial scale of the camera.\n * @property - Initial Euler rotation angles(x, y, z) of the camera in degree.\n * @property - Initial perspective of the camera.\n * @property - Initial rotate offset of the camera.\n */\nexport interface Options {\n  position: number[];\n  scale: number[];\n  rotation: number[];\n  perspective: number;\n  rotateOffset: number;\n}\n\nexport interface Offset {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\n/**\n * @typedef\n * @property - CSS [transition-property](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-property) to apply.\n * @property - CSS [transition-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function) to apply.\n * @property - CSS [transition-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-delay) to apply.\n */\nexport interface UpdateOption {\n  property: string;\n  timingFunction: string;\n  delay: string;\n}\n"
  },
  {
    "path": "src/utils/helper.ts",
    "content": "import { mat4, vec3 } from 'gl-matrix';\nimport { ELEMENT_NOT_EXIST, MUST_STRING_OR_ELEMENT } from '../constants/error';\nimport { Matrix4x4, Offset } from '../types';\n\nexport const getElement = (el: string | HTMLElement, baseElement?: HTMLElement): HTMLElement => {\n    if (typeof el === 'string') {\n        const queryResult = baseElement\n          ? baseElement.querySelector(el)\n          : document.querySelector(el);\n        if (!queryResult) {\n            throw new Error(ELEMENT_NOT_EXIST(el));\n        }\n        return queryResult as HTMLElement;\n    } else if (el.nodeName && el.nodeType === 1) {\n        return el;\n    } else {\n        throw new Error(MUST_STRING_OR_ELEMENT(el));\n    }\n};\n\nexport function applyCSS(element: HTMLElement, cssObj: { [keys: string]: string }): void {\n  Object.keys(cssObj).forEach(property => {\n    (element.style as any)[property] = cssObj[property];\n  });\n}\n\nexport function getTransformMatrix(elStyle: CSSStyleDeclaration): mat4 {\n  const trVal = elStyle.getPropertyValue('transform');\n  const transformStr = /\\(((\\s|\\S)+)\\)/.exec(trVal);\n  const matrixVal = transformStr\n    ? transformStr[1].split(',').map(val => parseFloat(val)) as Matrix4x4\n    : [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] as Matrix4x4;\n  if (matrixVal.length === 16 ) {\n    return mat4.fromValues(...matrixVal);\n  } else {\n    // Convert 2d matrix(length 6) to 3d\n    const matrix = mat4.create();\n    mat4.identity(matrix);\n\n    matrix[0] = matrixVal[0];\n    matrix[1] = matrixVal[1];\n    matrix[4] = matrixVal[2];\n    matrix[5] = matrixVal[3];\n    matrix[12] = matrixVal[4];\n    matrix[13] = matrixVal[5];\n\n    return matrix;\n  }\n}\n\nexport function getOffsetFromParent(currentOffset: Offset, parentOffset: Offset): vec3 {\n  const offsetLeft = currentOffset.left + (currentOffset.width - parentOffset.width) / 2;\n  const offsetTop = currentOffset.top + (currentOffset.height - parentOffset.height) / 2;\n\n  return vec3.fromValues(offsetLeft, offsetTop, 0);\n}\n\nexport function getRotateOffset(elStyle: CSSStyleDeclaration, currentOffset: Offset): vec3 {\n  const axis = (elStyle.transformOrigin as string)\n    .split(' ')\n    .map(str => parseFloat(str.substring(0, str.length - 2)));\n  const ax = axis[0] - currentOffset.width / 2;\n  const ay = axis[1] - currentOffset.height / 2;\n\n  return vec3.fromValues(ax, ay, 0);\n}\n\nexport function findIndex<T>(iterable: T[], callback: (el: T) => boolean): number {\n  for (let i = 0; i < iterable.length; i += 1) {\n    const element = iterable[i];\n    if (element && callback(element)) {\n      return i;\n    }\n  }\n\n  return -1;\n}\n\n// return [0, 1, ...., max - 1]\nexport function range(max: number): number[] {\n  const counterArray: number[] = [];\n  for (let i = 0; i < max; i += 1) {\n    counterArray[i] = i;\n  }\n  return counterArray;\n}\n\nexport function clamp(val: number, min: number, max: number): number {\n  return Math.max(Math.min(val, max), min);\n}\n\nexport function assign(target: object, ...srcs: object[]): object {\n  srcs.forEach(source => {\n    Object.keys(source).forEach(key => {\n      const value = (source as any)[key];\n      (target as any)[key] = value;\n    });\n  });\n\n  return target;\n}\n"
  },
  {
    "path": "src/utils/math.ts",
    "content": "import { mat4, quat, vec3 } from 'gl-matrix';\nimport { clamp } from './helper';\n\nexport function degToRad(deg: number): number {\n  return Math.PI * deg / 180;\n}\n\nexport function radToDeg(rad: number): number {\n  return 180 * rad / Math.PI;\n}\n\n// From Three.js https://github.com/mrdoob/three.js/blob/dev/src/math/Euler.js\nexport function quatToEuler(q: quat): vec3 {\n  const rotM = mat4.create();\n  mat4.fromQuat(rotM, q);\n\n  const m11 = rotM[0];\n  const m12 = rotM[4];\n  // const m13 = rotM[8];\n  const m21 = rotM[1];\n  const m22 = rotM[5];\n  // const m23 = rotM[9];\n  const m31 = rotM[2];\n  const m32 = rotM[6];\n  const m33 = rotM[10];\n\n  const euler = vec3.create();\n\n  // ZYX\n  euler[1] = Math.asin(-clamp(m31, -1, 1));\n  if (Math.abs(m31) < 0.99999) {\n    euler[0] = Math.atan2(m32, m33);\n    euler[2] = Math.atan2(m21, m11);\n  } else {\n    euler[0] = 0;\n    euler[2] = Math.atan2(-m12, m22);\n  }\n\n  return euler.map(val => radToDeg(val)) as vec3;\n}\n"
  },
  {
    "path": "test/manual/css/common.css",
    "content": "html {\n  height: 100%;\n}\nbody {\n  width: 100%;\n  height: 100%;\n  min-height: 100%;\n  margin: 0;\n}\n\n/* .cc-camera {\n  transition: transform 1s;\n}\n\n.cc-world {\n  transition: transform 1s;\n} */\n#space {\n  transform-style: preserve-3d;\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n.cube {\n  display: inline-block;\n  width: 300px;\n  height: 300px;\n  position: absolute;\n  left: calc(50% - 150px);\n  top: calc(50% - 150px);\n  transform-style: preserve-3d;\n  transition: transform 3s;\n}\n#cube2 {\n  transform: translate3d(150px, 0, -150px) rotateY(-90deg);\n}\n#cube3 {\n  transform: translate3d(300px, 0, 0px) rotateY(180deg);\n}\n.cube-side {\n  width: 100%;\n  height: 100%;\n  background: rgb(114, 55, 55);\n  position: absolute;\n  left: 0; top: 0;\n}\n.cube-side.up {\n  background: #222;\n  transform-origin: top;\n  transform: rotateX(-90deg);\n}\n.cube-side.down {\n  background: #444;\n  transform-origin: bottom;\n  transform: rotateX(90deg);\n}\n.cube-side.left {\n  background: #666;\n  transform-origin: left;\n  transform: rotateY(90deg);\n}\n.cube-side.right {\n  background: #888;\n  transform-origin: right;\n  transform: rotateY(-90deg);\n}\n#cube1 .cube-side.right,\n#cube2 .cube-side.right {\n  background: transparent;\n}\n.cube-side.front {\n  background: url(\"../assets/cover.png\");\n  background-position: center;\n  background-repeat: no-repeat;\n  background-size: cover;\n}\n.cube-side.back {\n  background: url(https://thumbs.gfycat.com/IdealisticOilyFlicker-size_restricted.gif);\n  background-position: center;\n  background-repeat: no-repeat;\n  background-size: cover;\n  transform: translateZ(-300px);\n  transform-style: preserve-3d;\n}\n#rotated {\n  transform-style: preserve-3d;\n  transform: rotateZ(-45deg) translate3d(200px, -500px, 300px) rotateX(90deg);\n  /* transform-origin: 50px 30%; */\n  width: 50vw;\n  height: 50vh;\n  transform-origin: 0% 0%;\n  background: aquamarine;\n  backface-visibility: hidden;\n}\n#rotated2 {\n  transform-style: preserve-3d;\n  transform: rotateY(15deg) translate3d(200px, -500px, 300px) rotateZ(10deg);\n  transform-origin: 0% 50px;\n  width: 50vw;\n  height: 50vh;\n  background: chartreuse;\n  backface-visibility: hidden;\n}\n#rotated3 {\n  transform-style: preserve-3d;\n  transform: translate3d(200px, -500px, 300px) rotateX(30deg) rotateY(-30deg);\n  transform-origin: 0% 120px;\n  width: 50vw;\n  height: 50vh;\n  background: tomato;\n  backface-visibility: hidden;\n}\n"
  },
  {
    "path": "test/manual/test.html",
    "content": "<!DOCTYPE html>\n<html>\n  <title>CSS-Camera Demo Page</title>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"css/common.css\">\n</html>\n<body>\n  <div id=\"space\">\n    <div id=\"rotated\">\n      TEXT\n      <div id=\"rotated2\">\n        TEXT\n        <div id=\"rotated3\">\n          <input type=\"text\" />\n          TEXT\n        </div>\n      </div>\n    </div>˜\n    <div class=\"cube\" id=\"cube1\">\n      <div class=\"cube-side front\"></div>\n      <div class=\"cube-side back\"></div>\n      <div class=\"cube-side up\"></div>\n      <div class=\"cube-side down\"></div>\n      <div class=\"cube-side left\"></div>\n      <div class=\"cube-side right\"></div>\n    </div>\n    <div class=\"cube\" id=\"cube2\">\n      <div class=\"cube-side front\"></div>\n      <div class=\"cube-side back\"></div>\n      <div class=\"cube-side up\"></div>\n      <div class=\"cube-side down\"></div>\n      <div class=\"cube-side left\"></div>\n      <div class=\"cube-side right\"></div>\n    </div>\n    <div class=\"cube\" id=\"cube3\">\n      <div class=\"cube-side front\"></div>\n      <div class=\"cube-side back\"></div>\n      <div class=\"cube-side up\"></div>\n      <div class=\"cube-side down\"></div>\n      <div class=\"cube-side left\"></div>\n      <div class=\"cube-side right\"></div>\n    </div>\n  </div>\n  <script type=\"text/javascript\" src=\"../../lib/css-camera.pkgd.js\"></script>\n  <script type=\"text/javascript\">\n    var camera = new CSSCamera(\"#space\", {\n      perspective: 300,\n      rotateOffset: 0,\n      position: [0, 0, -150],\n    });\n    camera.update(0).then(async () => {\n      camera.rotate(0, 90, 0);\n      await camera.update(2000, {\n        timingFunction: 'linear'\n      });\n      camera.rotate(0, 90, 0);\n      await camera.update(2000, {\n        timingFunction: 'linear'\n      });\n      camera.rotate(0, 90, 0);\n      await camera.update(2000, {\n        timingFunction: 'linear'\n      });\n      camera.rotate(0, 90, 0);\n      await camera.update(2000, {\n        timingFunction: 'linear'\n      });\n    });\n    // camera.update(0).then(async () => {\n    //   camera.focus(rotated3)\n    //   await camera.update(3000);\n    // });\n\n\n    const front = document.querySelector(\".front\");\n    const back = document.querySelector(\".back\");\n    const cubeLeft = document.querySelector(\".left\");\n    const cubeRight = document.querySelector(\".right\");\n\n    // camera.focus(rotated3);\n    // camera.update(2000);\n\n    // var up = false,\n    // right = false,\n    // down = false,\n    // left = false,\n    // space = false,\n    // x = window.innerWidth/2,\n    // y = window.innerHeight/2;\n\n    // document.addEventListener('keydown', press);\n    // function press(e){\n    //   if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */){\n    //     up = true;\n    //   }\n    //   if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */){\n    //     right = true;\n    //   }\n    //   if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */){\n    //     down = true;\n    //   }\n    //   if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */){\n    //     left = true;\n    //   }\n    //   if (e.keyCode === 32 /* space */) {\n    //     space = true;\n    //   }\n    // }\n\n    // document.addEventListener('keyup', release);\n    // function release(e){\n    //   if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */){\n    //     up = false\n    //   }\n    //   if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */){\n    //     right = false\n    //   }\n    //   if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */){\n    //     down = false\n    //   }\n    //   if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */){\n    //     left = false\n    //   }\n    //   if (e.keyCode === 32 /* space */) {\n    //     space = false;\n    //   }\n    // }\n\n    // const prevMouseLocation = {\n    //   x, y,\n    // }\n    // window.onmousemove = e => {\n    //   const diffX = e.screenX - prevMouseLocation.x;\n    //   const diffY = e.screenY - prevMouseLocation.y;\n\n    //   camera.rotate(-diffY / 5, diffX / 5);\n    //   camera.update(0);\n\n    //   prevMouseLocation.x = e.screenX;\n    //   prevMouseLocation.y = e.screenY;\n    // }\n    // const keyLoop = () => {\n    //   if (up){\n    //     camera.translateLocal(0, 0, -5);\n    //   }\n    //   if (right){\n    //     camera.translateLocal(5, 0, 0);\n    //   }\n    //   if (down){\n    //     camera.translateLocal(0, 0, 5);\n    //   }\n    //   if (left){\n    //     camera.translateLocal(-5, 0, 0);\n    //   }\n    //   if (space) {\n    //     camera.translate(0, -5, 0);\n    //   }\n    //   camera.position = [camera.position[0], 0, camera.position[2]];\n    //   camera.update(0);\n    //   requestAnimationFrame(keyLoop);\n    // }\n    // keyLoop();\n\n    // const loop = async () => {\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -450);\n    //   await camera.update(1000);\n    //   camera.rotate(0, 90, 0);\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -300);\n    //   await camera.update(1000);\n    //   camera.rotate(0, 90, 0);\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -300);\n    //   await camera.update(1000);\n    //   camera.rotate(0, 180, 0);\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -300);\n    //   await camera.update(1000);\n    //   camera.rotate(0, -90, 0);\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -300);\n    //   await camera.update(1000);\n    //   camera.rotate(0, -90, 0);\n    //   await camera.update(1000);\n    //   camera.translateLocal(0, 0, -450);\n    //   await camera.update(1000);\n    //   camera.rotate(0, 180, 0);\n    //   await camera.update(1000);\n    //   loop();\n    // }\n    // loop();\n  </script>\n</body>\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"es5\",\n\n        // Module\n        \"module\": \"es2015\",\n        \"moduleResolution\": \"node\",\n        \"allowSyntheticDefaultImports\": true,\n        \"esModuleInterop\": true,\n\n        // Path\n        \"rootDir\": \"./src\",\n        \"outDir\": \"./lib/\",\n\n        // Delcaration\n        \"declaration\": true,\n        \"declarationDir\": \"./lib/declaration\",\n\n        // Log\n        \"pretty\": true,\n\n        // Lint\n        \"strict\": true,\n        \"allowUnreachableCode\": false,\n        \"allowUnusedLabels\": false,\n        \"noFallthroughCasesInSwitch\": true,\n        \"noImplicitReturns\": true,\n        \"noUnusedLocals\": true,\n        \"noUnusedParameters\": true,\n        \"newLine\": \"lf\",\n\n        // Outfile\n        \"charset\": \"utf8\",\n        \"removeComments\": true,\n        \"noEmitOnError\": true,\n\n        // SourceMap\n        \"sourceMap\": true,\n\n        // etc\n        \"importHelpers\": true,\n        \"downlevelIteration\": true\n    },\n    \"include\": [\n        \"./src/**/*.ts\"\n    ]\n  }\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n    \"defaultSeverity\": \"error\",\n    \"extends\": [\n      \"tslint:recommended\"\n    ],\n    \"rulesDirectory\": [\n      \"tslint-consistent-codestyle\",\n      \"tslint-eslint-rules\"\n    ],\n    \"rules\": {\n      \"quotemark\": [true, \"single\"],\n      \"variable-name\": false,\n      \"arrow-parens\": false,\n      \"object-literal-key-quotes\": false,\n      \"object-literal-sort-keys\": false,\n      \"object-literal-shorthand\": false,\n      \"ordered-imports\": false,\n      \"no-console\": false,\n      \"curly\": false,\n      \"adjacent-overload-signatures\": false,\n      \"no-bitwise\": false,\n      \"interface-name\": false,\n      \"max-line-length\": false\n    },\n    \"linterOptions\": {\n      \"exclude\": [\n        \"node_modules/**/*\"\n      ]\n    }\n  }\n"
  }
]