[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"useBuiltIns\": \"usage\",\n        \"corejs\": \"3.0.0\"\n      }\n    ]\n  ],\n  \"plugins\": [\n    \"@babel/plugin-syntax-dynamic-import\",\n    \"@babel/plugin-proposal-class-properties\"\n  ]\n}\n"
  },
  {
    "path": ".dependabot/config.yml",
    "content": "version: 1\nupdate_configs:\n  - package_manager: \"javascript\"\n    directory: \"/\"\n    update_schedule: \"daily\"\n    automerged_updates:\n      - match:\n          dependency_type: \"all\"\n          update_type: \"semver:minor\"\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"node\": true\n  },\n  \"parserOptions\": {\n    \"ecmaVersion\": 6,\n    \"sourceType\": \"module\"\n  },\n  \"rules\": {\n    \"semi\": 2\n  }\n}"
  },
  {
    "path": ".gitignore",
    "content": "# dependencies\nnode_modules\n\n# production\nbuild\npublic\n\n# misc\n.DS_Store\n\nnpm-debug.log\nyarn-error.log\nyarn.lock\n.yarnclean\n.vscode\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"lts/*\"\n  - \"node\"\nscript: npm run build\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2020 Kyle Wetton\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": "![](screenshots/logo.jpg)\n\nA fully responsive slider to compare before and after images for grading, retouching and all else. Mobile and fluid container friendly! [Check out live examples](https://image-compare-viewer.netlify.app/)\n\n![](screenshots/eg-1.jpg)\n\n![](screenshots/eg-2.jpg)\n\n### Installation\n\n```\nnpm install image-compare-viewer --save\n```\n\nStylesheet is available at \"node_modules/image-compare-viewer/src/styles/index.scss\" or \"node_modules/image-compare-viewer/dist/image-compare-viewer.min.css\"\n\n### CDN\n\n#### Javascript\n\n```\n<script src=\"https://unpkg.com/image-compare-viewer/dist/image-compare-viewer.min.js\"></script>\n```\n\n#### CSS\n\n```\n<link type=\"text/css\" href=\"https://unpkg.com/image-compare-viewer/dist/image-compare-viewer.min.css\">\n```\n\n### Documentation\n\n[Installation, usage, options and examples](https://image-compare-viewer.netlify.app/)\n"
  },
  {
    "path": "__.browserslistrc",
    "content": "[production staging]\n>5%\nlast 2 versions\nFirefox ESR\nnot ie < 11\n\n[development]\nlast 1 chrome version\nlast 1 firefox version\nlast 1 edge version\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"image-compare-viewer\",\n  \"version\": \"1.7.0\",\n  \"description\": \"A fully responsive slider to compare before and after images for grading, retouching and all else. Mobile and fluid container friendly!\",\n  \"scripts\": {\n    \"build\": \"webpack --mode production --config webpack/webpack.config.prod.js\",\n    \"start\": \"webpack-dev-server --host 127.0.0.1 --open --config webpack/webpack.config.dev.js\",\n    \"pack\": \"webpack --mode production --config webpack.config.js\"\n  },\n  \"main\": \"src/scripts/index.js\",\n  \"unpkg\": \"dist/image-compare-viewer.min.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/kylewetton/image-compare-viewer\"\n  },\n  \"keywords\": [\n    \"before\",\n    \"after\",\n    \"compare\",\n    \"grading\",\n    \"images\",\n    \"frontend\",\n    \"es6\",\n    \"javascript\",\n    \"vanilla\"\n  ],\n  \"author\": \"Kyle Wetton\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/kylewetton/image-compare-viewer\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\"\n  ],\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.6.4\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.5.5\",\n    \"@babel/plugin-syntax-dynamic-import\": \"^7.2.0\",\n    \"@babel/preset-env\": \"^7.6.3\",\n    \"babel-loader\": \"^8.0.6\",\n    \"clean-webpack-plugin\": \"^4.0.0\",\n    \"copy-webpack-plugin\": \"^11.0.0\",\n    \"css-loader\": \"^6.7.1\",\n    \"eslint\": \"^8.18.0\",\n    \"eslint-webpack-plugin\": \"^3.2.0\",\n    \"file-loader\": \"^6.2.0\",\n    \"html-webpack-plugin\": \"^5.5.0\",\n    \"mini-css-extract-plugin\": \"^2.6.1\",\n    \"node-sass\": \"^9.0.0\",\n    \"sass-loader\": \"^13.0.0\",\n    \"style-loader\": \"^3.3.1\",\n    \"webpack\": \"^5.73.0\",\n    \"webpack-cli\": \"^4.10.0\",\n    \"webpack-dev-server\": \"^4.9.2\",\n    \"webpack-merge\": \"^5.8.0\"\n  },\n  \"dependencies\": {\n    \"@babel/polyfill\": \"^7.6.0\",\n    \"autoprefixer\": \"^10.4.7\",\n    \"body-scroll-lock-upgrade\": \"^1.1.0\",\n    \"core-js\": \"^3.3.2\",\n    \"postcss-loader\": \"^7.0.0\"\n  }\n}\n"
  },
  {
    "path": "src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <title>Image Compare Viewer</title>\n    <style>\n      body {\n        font-family: system-ui;\n      }\n      .title {\n        position: absolute;\n        z-index: 10;\n        width: 100%;\n        text-align: center;\n        line-height: 1;\n        font-size: 6rem;\n        margin-top: 4rem;\n        color: white;\n        text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);\n        user-select: none;\n      }\n      #image-compare {\n        display: flex;\n        justify-content: center;\n        align-items: center;\n      }\n    </style>\n  </head>\n  <body>\n    <!-- -->\n    <div class=\"container\">\n      <div class=\"image-compare\">\n        <img\n          src=\"https://image-compare-viewer.netlify.app/public/before-cgi.jpg\"\n          alt=\"\"\n        />\n        <img\n          src=\"https://image-compare-viewer.netlify.app/public/after-cgi.jpg\"\n          alt=\"\"\n        />\n      </div>\n    </div>\n\n    <!-- -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/scripts/index.js",
    "content": "// uncomment for packing\n// import \"../styles/index.scss\";\nimport { disableBodyScroll, enableBodyScroll } from \"body-scroll-lock-upgrade\";\n\nclass ImageCompare {\n  constructor(el, settings = {}) {\n    const defaults = {\n      controlColor: \"#FFFFFF\",\n      controlShadow: true,\n      addCircle: false,\n      addCircleBlur: true,\n      showLabels: false,\n      labelOptions: {\n        before: \"Before\",\n        after: \"After\",\n        onHover: false,\n      },\n      smoothing: true,\n      smoothingAmount: 100,\n      hoverStart: false,\n      verticalMode: false,\n      startingPoint: 50,\n      fluidMode: false,\n    };\n\n    this.settings = Object.assign(defaults, settings);\n\n    this.safariAgent =\n      navigator.userAgent.indexOf(\"Safari\") != -1 &&\n      navigator.userAgent.indexOf(\"Chrome\") == -1;\n\n    this.el = el;\n    this.images = {};\n    this.wrapper = null;\n    this.control = null;\n    this.arrowContainer = null;\n    this.arrowAnimator = [];\n    this.active = false;\n    this.slideWidth = 50;\n    this.lineWidth = 2;\n    this.arrowCoordinates = {\n      circle: [5, 3],\n      standard: [8, 0],\n    };\n  }\n\n  mount() {\n    // Temporarily disable Safari smoothing\n    if (this.safariAgent) {\n      this.settings.smoothing = false;\n    }\n\n    this._shapeContainer();\n    this._getImages();\n    this._buildControl();\n    this._events();\n  }\n\n  _events() {\n    let bodyStyles = ``;\n\n    // Desktop events\n    this.el.addEventListener(\"mousedown\", (ev) => {\n      this._activate(true);\n      document.body.classList.add(\"icv__body\");\n      disableBodyScroll(this.el, {reserveScrollBarGap: true});\n      this._slideCompare(ev);\n    });\n    this.el.addEventListener(\n      \"mousemove\",\n      (ev) => this.active && this._slideCompare(ev)\n    );\n\n    this.el.addEventListener(\"mouseup\", () => this._activate(false));\n    document.body.addEventListener(\"mouseup\", () => {\n      document.body.classList.remove(\"icv__body\");\n      enableBodyScroll(this.el);\n      this._activate(false);\n    });\n\n    // Mobile events\n\n    this.control.addEventListener(\"touchstart\", (e) => {\n      this._activate(true);\n      document.body.classList.add(\"icv__body\");\n      disableBodyScroll(this.el, {reserveScrollBarGap: true});\n    });\n\n    this.el.addEventListener(\"touchmove\", (ev) => {\n      this.active && this._slideCompare(ev);\n    });\n    this.el.addEventListener(\"touchend\", () => {\n      this._activate(false);\n      document.body.classList.remove(\"icv__body\");\n      enableBodyScroll(this.el);\n    });\n\n    // hover\n\n    this.el.addEventListener(\"mouseenter\", () => {\n      this.settings.hoverStart && this._activate(true);\n      let coord = this.settings.addCircle\n        ? this.arrowCoordinates.circle\n        : this.arrowCoordinates.standard;\n\n      this.arrowAnimator.forEach((anim, i) => {\n        anim.style.cssText = `\n        ${\n          this.settings.verticalMode\n            ? `transform: translateY(${coord[1] * (i === 0 ? 1 : -1)}px);`\n            : `transform: translateX(${coord[1] * (i === 0 ? 1 : -1)}px);`\n        }\n        `;\n      });\n    });\n\n    this.el.addEventListener(\"mouseleave\", () => {\n      let coord = this.settings.addCircle\n        ? this.arrowCoordinates.circle\n        : this.arrowCoordinates.standard;\n\n      this.arrowAnimator.forEach((anim, i) => {\n        anim.style.cssText = `\n        ${\n          this.settings.verticalMode\n            ? `transform: translateY(${\n                i === 0 ? `${coord[0]}px` : `-${coord[0]}px`\n              });`\n            : `transform: translateX(${\n                i === 0 ? `${coord[0]}px` : `-${coord[0]}px`\n              });`\n        }\n        `;\n      });\n    });\n  }\n\n\n  _slideCompare(ev) {\n    let bounds = this.el.getBoundingClientRect();\n    let x =\n      ev.touches !== undefined\n        ? ev.touches[0].clientX - bounds.left\n        : ev.clientX - bounds.left;\n    let y =\n      ev.touches !== undefined\n        ? ev.touches[0].clientY - bounds.top\n        : ev.clientY - bounds.top;\n\n    let position = this.settings.verticalMode\n      ? (y / bounds.height) * 100\n      : (x / bounds.width) * 100;\n\n    if (position >= 0 && position <= 100) {\n      this.settings.verticalMode\n        ? (this.control.style.top = `calc(${position}% - ${\n            this.slideWidth / 2\n          }px)`)\n        : (this.control.style.left = `calc(${position}% - ${\n            this.slideWidth / 2\n          }px)`);\n\n      if (this.settings.fluidMode) {\n        this.settings.verticalMode\n          ? (this.wrapper.style.clipPath = `inset(0 0 ${100 - position}% 0)`)\n          : (this.wrapper.style.clipPath = `inset(0 0 0 ${position}%)`);\n      } else {\n        this.settings.verticalMode\n          ? (this.wrapper.style.height = `calc(${position}%)`)\n          : (this.wrapper.style.width = `calc(${100 - position}%)`);\n      }\n    }\n  }\n\n  _activate(state) {\n    this.active = state;\n  }\n\n  _shapeContainer() {\n    let imposter = document.createElement(\"div\");\n    let label_l = document.createElement(\"span\");\n    let label_r = document.createElement(\"span\");\n\n    label_l.classList.add(\"icv__label\", \"icv__label-before\", \"keep\");\n    label_r.classList.add(\"icv__label\", \"icv__label-after\", \"keep\");\n\n    if (this.settings.labelOptions.onHover) {\n      label_l.classList.add(\"on-hover\");\n      label_r.classList.add(\"on-hover\");\n    }\n\n    if (this.settings.verticalMode) {\n      label_l.classList.add(\"vertical\");\n      label_r.classList.add(\"vertical\");\n    }\n\n    label_l.innerHTML = this.settings.labelOptions.before || \"Before\";\n    label_r.innerHTML = this.settings.labelOptions.after || \"After\";\n\n    if (this.settings.showLabels) {\n      this.el.appendChild(label_l);\n      this.el.appendChild(label_r);\n    }\n\n    this.el.classList.add(\n      `icv`,\n      this.settings.verticalMode\n        ? `icv__icv--vertical`\n        : `icv__icv--horizontal`,\n      this.settings.fluidMode ? `icv__is--fluid` : `standard`\n    );\n\n    imposter.classList.add(\"icv__imposter\");\n\n    this.el.appendChild(imposter);\n  }\n\n  _buildControl() {\n    let control = document.createElement(\"div\");\n    let uiLine = document.createElement(\"div\");\n    let arrows = document.createElement(\"div\");\n    let circle = document.createElement(\"div\");\n\n    const arrowSize = \"20\";\n\n    arrows.classList.add(\"icv__theme-wrapper\");\n\n    for (var idx = 0; idx <= 1; idx++) {\n      let animator = document.createElement(`div`);\n\n      let arrow = `<svg\n      height=\"15\"\n      width=\"15\"\n       style=\"\n       transform: \n       scale(${this.settings.addCircle ? 0.7 : 1.5})  \n       rotateZ(${\n         idx === 0\n           ? this.settings.verticalMode\n             ? `-90deg`\n             : `180deg`\n           : this.settings.verticalMode\n           ? `90deg`\n           : `0deg`\n       }); height: ${arrowSize}px; width: ${arrowSize}px;\n       \n       ${\n         this.settings.controlShadow\n           ? `\n       -webkit-filter: drop-shadow( 0px 3px 5px rgba(0, 0, 0, .33));\n       filter: drop-shadow( 0px ${\n         idx === 0 ? \"-3px\" : \"3px\"\n       } 5px rgba(0, 0, 0, .33));\n       `\n           : ``\n       }\n       \"\n       xmlns=\"http://www.w3.org/2000/svg\" data-name=\"Layer 1\" viewBox=\"0 0 15 15\">\n       <path ${\n         this.settings.addCircle\n           ? `fill=\"transparent\"`\n           : `fill=\"${this.settings.controlColor}\"`\n       }\n       stroke=\"${this.settings.controlColor}\"\n       stroke-linecap=\"round\"\n       stroke-width=\"${this.settings.addCircle ? 3 : 0}\"\n       d=\"M4.5 1.9L10 7.65l-5.5 5.4\"\n       />\n     </svg>`;\n\n      animator.innerHTML += arrow;\n      this.arrowAnimator.push(animator);\n      arrows.appendChild(animator);\n    }\n\n    let coord = this.settings.addCircle\n      ? this.arrowCoordinates.circle\n      : this.arrowCoordinates.standard;\n\n    this.arrowAnimator.forEach((anim, i) => {\n      anim.classList.add(\"icv__arrow-wrapper\");\n\n      anim.style.cssText = `\n      ${\n        this.settings.verticalMode\n          ? `transform: translateY(${\n              i === 0 ? `${coord[0]}px` : `-${coord[0]}px`\n            });`\n          : `transform: translateX(${\n              i === 0 ? `${coord[0]}px` : `-${coord[0]}px`\n            });`\n      }\n      `;\n    });\n\n    control.classList.add(\"icv__control\");\n\n    control.style.cssText = `\n    ${this.settings.verticalMode ? `height` : `width `}: ${this.slideWidth}px;\n    ${this.settings.verticalMode ? `top` : `left `}: calc(${\n      this.settings.startingPoint\n    }% - ${this.slideWidth / 2}px);\n    ${\n      \"ontouchstart\" in document.documentElement\n        ? ``\n        : this.settings.smoothing\n        ? `transition: ${this.settings.smoothingAmount}ms ease-out;`\n        : ``\n    }\n    `;\n\n    uiLine.classList.add(\"icv__control-line\");\n\n    uiLine.style.cssText = `\n      ${this.settings.verticalMode ? `height` : `width `}: ${this.lineWidth}px;\n      background: ${this.settings.controlColor};\n        ${\n          this.settings.controlShadow\n            ? `box-shadow: 0px 0px 15px rgba(0,0,0,0.33);`\n            : ``\n        }\n    `;\n\n    let uiLine2 = uiLine.cloneNode(true);\n\n    circle.classList.add(\"icv__circle\");\n    circle.style.cssText = `\n\n      ${\n        this.settings.addCircleBlur &&\n        `-webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px)`\n      };\n      \n      border: ${this.lineWidth}px solid ${this.settings.controlColor};\n      ${\n        this.settings.controlShadow &&\n        `box-shadow: 0px 0px 15px rgba(0,0,0,0.33)`\n      };\n    `;\n\n    control.appendChild(uiLine);\n    this.settings.addCircle && control.appendChild(circle);\n    control.appendChild(arrows);\n    control.appendChild(uiLine2);\n\n    this.arrowContainer = arrows;\n\n    this.control = control;\n    this.el.appendChild(control);\n  }\n\n  _getImages() {\n    let children = this.el.querySelectorAll(\"img, video, .keep\");\n    this.el.innerHTML = \"\";\n    children.forEach((img) => {\n      this.el.appendChild(img);\n    });\n\n    let childrenImages = [...children].filter(\n      (element) => [\"img\", \"video\"].includes(element.nodeName.toLowerCase())\n    );\n\n    //  this.settings.verticalMode && [...children].reverse();\n    this.settings.verticalMode && childrenImages.reverse();\n\n    for (let idx = 0; idx <= 1; idx++) {\n      let child = childrenImages[idx];\n\n      child.classList.add(\"icv__img\");\n      child.classList.add(idx === 0 ? `icv__img-a` : `icv__img-b`);\n\n      if (idx === 1) {\n        let wrapper = document.createElement(\"div\");\n        let afterUrl = childrenImages[1].src;\n        wrapper.classList.add(\"icv__wrapper\");\n        wrapper.style.cssText = `\n            width: ${100 - this.settings.startingPoint}%; \n            height: ${this.settings.startingPoint}%;\n\n            ${\n              \"ontouchstart\" in document.documentElement\n                ? ``\n                : this.settings.smoothing\n                ? `transition: ${this.settings.smoothingAmount}ms ease-out;`\n                : ``\n            }\n            ${\n              this.settings.fluidMode &&\n              `background-image: url(${afterUrl}); clip-path: inset(${\n                this.settings.verticalMode\n                  ? ` 0 0 ${100 - this.settings.startingPoint}% 0`\n                  : `0 0 0 ${this.settings.startingPoint}%`\n              })`\n            }\n        `;\n\n        wrapper.appendChild(child);\n        this.wrapper = wrapper;\n        this.el.appendChild(this.wrapper);\n      }\n    }\n    if (this.settings.fluidMode) {\n      let url = childrenImages[0].src;\n      let fluidWrapper = document.createElement(\"div\");\n      fluidWrapper.classList.add(\"icv__fluidwrapper\");\n      fluidWrapper.style.cssText = `\n \n        background-image: url(${url});\n        \n      `;\n      this.el.appendChild(fluidWrapper);\n    }\n  }\n}\n\n// const el = document.querySelectorAll(\".image-compare\");\n\n// el.forEach((viewer) => {\n//   let v = new ImageCompare(viewer, {\n//     controlShadow: false,\n//     addCircle: true,\n//     addCircleBlur: true,\n//     fluidMode: false,\n//     showLabels: true,\n//     labelOptions: {\n//       onHover: true,\n//       before: \"Draft\",\n//       after: \"Final\",\n//     },\n//   }).mount();\n// });\n\nexport default ImageCompare;\n"
  },
  {
    "path": "src/styles/index.scss",
    "content": ".icv {\n  position: relative;\n  overflow: hidden;\n  cursor: row-resize;\n\n  &__icv--vertical {\n    cursor: row-resize;\n  }\n\n  &__icv--horizontal {\n    cursor: col-resize;\n  }\n\n  &__img {\n    pointer-events: none;\n    -khtml-user-select: none;\n    -o-user-select: none;\n    -moz-user-select: none;\n    -webkit-user-select: none;\n    user-select: none;\n    max-width: none;\n    width: 100%;\n    margin: 0 !important;\n    padding: 0 !important;\n    border: 0 !important;\n    border-radius: 0 !important;\n    top: 0;\n    display: block;\n  }\n\n  &__is--fluid &__img {\n    display: none;\n  }\n\n  &__img-a {\n    height: auto;\n    position: static;\n    z-index: 1;\n    left: 0px;\n  }\n\n  &__img-b {\n    height: 100%;\n    position: absolute;\n    z-index: 2;\n    left: auto;\n    right: 0px;\n    width: auto;\n  }\n\n  &__icv--vertical &__img-b {\n    width: 100%;\n    height: auto;\n  }\n\n  &__imposter {\n    z-index: 4;\n    position: absolute;\n    top: 0px;\n    left: 0px;\n    width: 100%;\n    height: 100%;\n  }\n\n  &__wrapper {\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    right: 0px;\n    top: 0px;\n    overflow: hidden;\n    background-size: cover;\n    background-position: center center;\n    z-index: 3;\n  }\n\n  &__is--fluid &__wrapper,\n  &__icv--vertical &__wrapper {\n    width: 100% !important;\n  }\n\n  &__is--fluid &__wrapper,\n  &__icv--horizontal &__wrapper {\n    height: 100% !important;\n  }\n\n  &__fluidwrapper {\n    background-size: cover;\n    background-position: center;\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n  }\n\n  &__control {\n    position: absolute;\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n    box-sizing: border-box;\n    height: 100%;\n    top: 0px;\n    z-index: 5;\n  }\n\n  &__icv--vertical &__control {\n    flex-direction: row;\n    left: 0;\n    width: 100%;\n  }\n\n  &__control-line {\n    height: 50%;\n    width: 2px;\n    z-index: 6;\n  }\n\n  &__icv--vertical &__control-line {\n    width: 50%;\n  }\n\n  &__theme-wrapper {\n    width: 100%;\n    height: 100%;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    position: absolute;\n    transition: all 0.1s ease-out 0s;\n    z-index: 5;\n  }\n\n  &__icv--vertical &__theme-wrapper {\n    flex-direction: column;\n  }\n\n  &__arrow-wrapper {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    transition: all 0.1s ease-out 0s;\n  }\n\n  &__arrow-a {\n    transform: scale(1.5) rotateZ(180deg);\n    height: 20px;\n    width: 20px;\n    -webkit-filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.33));\n    filter: drop-shadow(0px -3px 5px rgba(0, 0, 0, 0.33));\n  }\n  &__arrow-b {\n    transform: scale(1.5) rotateZ(0deg);\n    height: 20px;\n    width: 20px;\n\n    -webkit-filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.33));\n    filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.33));\n  }\n\n  &__circle {\n    width: 50px;\n    height: 50px;\n    box-sizing: border-box;\n    flex-shrink: 0;\n    border-radius: 999px;\n  }\n\n  &__label {\n    position: absolute;\n    bottom: 1rem;\n    z-index: 12;\n    background: rgba(0, 0, 0, 0.33);\n    color: white;\n    border-radius: 3px;\n    padding: 0.5rem 0.75rem;\n    font-size: 0.85rem;\n    user-select: none;\n  }\n\n  &__label.vertical {\n    bottom: auto;\n    left: 1rem;\n  }\n\n  &__label.on-hover {\n    transform: scale(0);\n    transition: 0.25s cubic-bezier(0.68, 0.26, 0.58, 1.22);\n  }\n\n  &:hover &__label.on-hover {\n    transform: scale(1);\n  }\n\n  &__label-before {\n    left: 1rem;\n  }\n  &__label-after {\n    right: 1rem;\n  }\n  &__label-before.vertical {\n    top: 1rem;\n  }\n  &__label-after.vertical {\n    bottom: 1rem;\n    right: auto;\n  }\n\n  &__body {\n    -webkit-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n  }\n}\n"
  },
  {
    "path": "webpack/webpack.common.js",
    "content": "const Path = require('path');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst CopyWebpackPlugin = require('copy-webpack-plugin');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n\nmodule.exports = {\n  entry: {\n    app: Path.resolve(__dirname, '../src/scripts/index.js')\n  },\n  output: {\n    path: Path.join(__dirname, '../build'),\n    filename: 'js/[name].js'\n  },\n  optimization: {\n    splitChunks: {\n      chunks: 'all',\n      name: false\n    }\n  },\n  plugins: [\n    new CleanWebpackPlugin(),\n    new CopyWebpackPlugin({\n      patterns: [\n        { from: Path.resolve(__dirname, '../public'), to: 'public' }\n      ]\n    }),\n    new HtmlWebpackPlugin({\n      template: Path.resolve(__dirname, '../src/index.html')\n    })\n  ],\n  resolve: {\n    alias: {\n      '~': Path.resolve(__dirname, '../src')\n    }\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.mjs$/,\n        include: /node_modules/,\n        type: 'javascript/auto'\n      },\n      {\n        test: /\\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\\?.*)?$/,\n        use: {\n          loader: 'file-loader',\n          options: {\n            name: '[path][name].[ext]'\n          }\n        }\n      },\n    ]\n  }\n};\n"
  },
  {
    "path": "webpack/webpack.config.dev.js",
    "content": "const ESLintPlugin = require('eslint-webpack-plugin');\nconst Path = require('path');\nconst Webpack = require('webpack');\nconst { merge } = require('webpack-merge');\nconst common = require('./webpack.common.js');\n\nmodule.exports = merge(common, {\n  mode: 'development',\n  devtool: 'eval-cheap-source-map',\n  output: {\n    chunkFilename: 'js/[name].chunk.js'\n  },\n  plugins: [\n    new Webpack.DefinePlugin({\n      'process.env.NODE_ENV': JSON.stringify('development')\n    }),\n    new ESLintPlugin({\n      files: '../src/**/*.js',\n      emitWarning: true\n    })\n  ],\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        include: Path.resolve(__dirname, '../src'),\n        loader: 'babel-loader'\n      },\n      {\n        test: /\\.s?css$/i,\n        use: [\n          'style-loader', {\n            loader: 'css-loader',\n            options: { sourceMap: true }\n          },\n          'sass-loader'\n        ]\n      }\n    ]\n  }\n});\n"
  },
  {
    "path": "webpack/webpack.config.prod.js",
    "content": "const Webpack = require(\"webpack\");\nconst { merge } = require(\"webpack-merge\");\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\nconst common = require(\"./webpack.common.js\");\n\nmodule.exports = merge(common, {\n  mode: \"production\",\n  devtool: \"source-map\",\n  stats: \"errors-only\",\n  bail: true,\n  output: {\n    filename: \"js/.[chunkhash:8].js\",\n    chunkFilename: \"js/[name].[chunkhash:8].chunk.js\",\n  },\n  plugins: [\n    new Webpack.DefinePlugin({\n      \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n    }),\n    new Webpack.optimize.ModuleConcatenationPlugin(),\n    new MiniCssExtractPlugin({\n      filename: \"bundle.css\",\n    }),\n  ],\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        exclude: /node_modules/,\n        use: \"babel-loader\",\n      },\n      {\n        test: /\\.s?css/i,\n        use: [MiniCssExtractPlugin.loader, \"css-loader\", \"sass-loader\"],\n      },\n    ],\n  },\n});\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require(\"path\");\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\nconst Webpack = require(\"webpack\");\nconst autoprefixer = require(\"autoprefixer\");\n\nmodule.exports = [\"source-map\"].map((devtool) => ({\n  mode: \"production\",\n  entry: \"./src/scripts/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"dist\"),\n    filename: \"image-compare-viewer.min.js\",\n    library: \"ImageCompare\",\n    libraryTarget: \"umd\",\n    libraryExport: \"default\",\n    umdNamedDefine: true,\n  },\n  devtool,\n  optimization: {\n    runtimeChunk: false,\n  },\n  plugins: [\n    new Webpack.DefinePlugin({\n      \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n    }),\n    new Webpack.optimize.ModuleConcatenationPlugin(),\n    new MiniCssExtractPlugin({\n      filename: \"image-compare-viewer.min.css\",\n    }),\n  ],\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        exclude: /node_modules/,\n        use: \"babel-loader\",\n      },\n      {\n        test: /\\.s?css/i,\n        use: [\n          MiniCssExtractPlugin.loader,\n          \"css-loader\",\n          {\n            loader: \"postcss-loader\",\n            options: {\n              postcssOptions: {\n                plugins: [[autoprefixer()]],\n              },\n            },\n          },\n          \"sass-loader\",\n        ],\n      },\n    ],\n  },\n}));\n"
  }
]