[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n*.zip"
  },
  {
    "path": "README.md",
    "content": "# Draggable Menu with Image Grid Previews\n\nA draggable inline menu with a scattered thumbnail preview of an image grid.\n\n![Draggable Menu](https://tympanus.net/codrops/wp-content/uploads/2019/06/DraggableMenu_feat.jpg)\n\n[Article on Codrops](https://tympanus.net/codrops/?p=40926)\n\n[Demo](http://tympanus.net/Development/DraggableMenu/)\n\n## Credits\n\n*   Fonts used in the demo: [Niveau Grotesk](https://fonts.adobe.com/fonts/niveau-grotesk)\n*   [TweenMax](https://greensock.com/tweenmax) by Greensock\n*   [Draggabilly](https://draggabilly.desandro.com/) by Dave DeSandro\n*   [imagesLoaded](https://imagesloaded.desandro.com/) by Dave DeSandro\n*   Images from [Unsplash.com](https://unsplash.com/)\n\n## License\nThis resource can be used freely if integrated or build upon in personal or commercial projects such as websites, web apps and web templates intended for sale. It is not allowed to take the resource \"as-is\" and sell it, redistribute, re-publish it, or sell \"pluginized\" versions of it. Free plugins built using this resource should have a visible mention and link to the original work. Always consider the licenses of all included libraries, scripts and images used.\n\n## Misc\n\nFollow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [Google+](https://plus.google.com/101095823814290637419), [GitHub](https://github.com/codrops), [Pinterest](http://www.pinterest.com/codrops/), [Instagram](https://www.instagram.com/codropsss/)\n\n\n[© Codrops 2019](http://www.codrops.com)\n\n\n\n\n\n"
  },
  {
    "path": "css/base.css",
    "content": "*,\r\n*::after,\r\n*::before {\r\n\tbox-sizing: border-box;\r\n}\r\n\r\n:root {\r\n\tfont-size: 14px;\r\n}\r\n\r\nbody {\r\n\tmargin: 0;\r\n\t--color-text: #1E1E1E;\r\n\t--color-bg: #f1f1f1;\r\n\t--color-link: #de6565;\r\n\t--color-link-hover: #1E1E1E;\r\n\t--color-menu-stroke: #1E1E1E;\r\n\t--color-menu-item: #1E1E1E;\r\n\t--color-explore: #1E1E1E;\r\n\tcolor: var(--color-text);\r\n\tbackground-color: var(--color-bg);\r\n\tfont-family: niveau-grotesk, sans-serif;\r\n\t-webkit-font-smoothing: antialiased;\r\n\t-moz-osx-font-smoothing: grayscale;\r\n\toverflow: scroll;\r\n\toverflow-x: hidden;\r\n}\r\n\r\n.cursor {\r\n\tdisplay: none;\r\n}\r\n\r\nmain {\r\n\twidth: 100%;\r\n\toverflow: hidden;\r\n}\r\n\r\n/* Page Loader */\r\n.js .loading::before {\r\n\tcontent: '';\r\n\tposition: fixed;\r\n\tz-index: 100000;\r\n\ttop: 0;\r\n\tleft: 0;\r\n\twidth: 100%;\r\n\theight: 100%;\r\n\tbackground: var(--color-bg);\r\n}\r\n\r\n.js .loading::after {\r\n\tcontent: '';\r\n\tposition: fixed;\r\n\tz-index: 100000;\r\n\ttop: 50%;\r\n\tleft: 50%;\r\n\twidth: 60px;\r\n\theight: 60px;\r\n\tmargin: -30px 0 0 -30px;\r\n\tpointer-events: none;\r\n\tborder-radius: 50%;\r\n\topacity: 0.4;\r\n\tbackground: var(--color-link);\r\n\tanimation: loaderAnim 0.7s linear infinite alternate forwards;\r\n}\r\n\r\n@keyframes loaderAnim {\r\n\tto {\r\n\t\topacity: 1;\r\n\t\ttransform: scale3d(0.5,0.5,1);\r\n\t}\r\n}\r\n\r\na {\r\n\ttext-decoration: none;\r\n\tcolor: var(--color-link);\r\n\toutline: none;\r\n}\r\n\r\na:hover,\r\na:focus {\r\n\tcolor: var(--color-link-hover);\r\n\toutline: none;\r\n}\r\n\r\n.frame {\r\n\tpadding: 2rem 1rem;\r\n\ttext-align: center;\r\n\tposition: absolute;\r\n\ttop: 0;\r\n\tleft: 0;\r\n\twidth: 100%;\r\n\tz-index: 1000;\r\n\tpointer-events: none;\r\n}\r\n\r\n.frame__title {\r\n\tfont-size: 1rem;\r\n\tmargin: 0 0 1rem;\r\n}\r\n\r\n.frame__links {\r\n\tdisplay: inline;\r\n}\r\n\r\n.frame a {\r\n\tpointer-events: auto;\r\n\ttext-transform: lowercase;\r\n}\r\n\r\n.frame__links a:not(:last-child) {\r\n\tmargin-right: 1rem;\r\n}\r\n\r\n.frame__social {\r\n\tmargin: 1rem 0 0 0;\r\n}\r\n\r\n.frame__social-behance {\r\n\twidth: 20px;\r\n\tdisplay: inline-block;\r\n}\r\n\r\n.frame__pagetitle {\r\n\tfont-weight: bold;\r\n\tmargin: 0 0 1rem;\r\n}\r\n\r\n.menu-wrap {\r\n\tposition: absolute;\r\n\ttop: 0;\r\n\theight: 100%;\r\n\twidth: 100%;\r\n\toverflow: hidden;\r\n\tpointer-events: none;\r\n}\r\n\r\n.page--preview ~ .menu-wrap {\r\n\tpointer-events: auto;\r\n}\r\n\r\n.menu-draggable {\r\n\ttop: 0;\r\n\tleft: 0;\r\n    height: 100%;\r\n    width: 100%;\r\n    position: absolute;\r\n\tcursor: grab;\r\n}\r\n\r\n.menu-draggable:active {\r\n\tcursor: grabbing;\r\n}\r\n\r\n.menu {\r\n\tdisplay: flex;\r\n\twidth: 100vw;\r\n\theight: 100vh;\r\n\tposition: relative;\r\n\tjustify-content: flex-start;\r\n\talign-items: center;\r\n\twidth: -moz-fit-content;\r\n\twidth: fit-content;\r\n\tcounter-reset: menu-number;\r\n\tpointer-events: none;\r\n\twill-change: transform;\r\n}\r\n\r\n.menu__item {\r\n\tposition: relative;\r\n\ttext-align: center;\r\n\tmargin: 0 10vw 0 0;\r\n\t--counter-opacity: 0;\r\n\tcolor: var(--color-menu-item);\r\n\t-webkit-touch-callout: none;\r\n\t-webkit-user-select: none;\r\n\t-moz-user-select: none;\r\n\t-ms-user-select: none;\r\n\tuser-select: none;\r\n}\r\n\r\n.page--preview ~ .menu-wrap .menu__item {\r\n\t--counter-opacity: 1;\r\n}\r\n\r\n.menu__item::before {\r\n\tcounter-increment: menu-number;\r\n\tcontent: counter(menu-number, decimal-leading-zero);\r\n\tposition: absolute;\r\n\ttop: 0;\r\n\tright: 100%;\r\n\topacity: var(--counter-opacity);\r\n\ttransition: opacity 0.3s;\r\n}\r\n\r\n.menu__item--current {\r\n\tcolor: var(--color-menu-item);\r\n}\r\n\r\n.menu__item-link {\r\n\tcolor: currentColor;\r\n\tfont-weight: bold;\r\n\tfont-size: 12vw;\r\n\tdisplay: flex;\r\n\tline-height: 1.2;\r\n}\r\n\r\n.menu__item-explore {\r\n\tcursor: pointer;\r\n\tmargin: 0.5rem 0 0 0;\r\n\tdisplay: inline-block;\r\n\tcolor: var(--color-explore);\r\n\ttext-decoration: underline;\r\n\topacity: 0;\r\n\tpadding: 0.5rem 0.5rem 0;\r\n\twill-change: transform;\r\n}\r\n\r\n.menu__item-explore:hover,\r\n.menu__item-explore:focus {\r\n\ttext-decoration: none;\r\n}\r\n\r\n.page--preview ~ .menu-wrap .menu__item--current .menu__item-explore {\r\n\tpointer-events: auto;\r\n}\r\n\r\n.letter {\r\n\tposition: relative;\r\n\toverflow: hidden;\r\n\tdisplay: inline-block;\r\n}\r\n\r\n.letter__inner {\r\n\tdisplay: block;\r\n\twill-change: transform;\r\n}\r\n\r\n.letter__inner--stroke {\r\n\tposition: absolute;\r\n\tleft: 100%;\r\n\ttop: 0;\r\n\t-webkit-text-stroke: 1px var(--color-menu-stroke);\r\n\ttext-stroke: 1px var(--color-menu-stroke);\r\n\t-webkit-text-fill-color: transparent;\r\n\ttext-fill-color: transparent;\r\n\tcolor: transparent;\r\n}\r\n\r\n.menu__item--current .letter__inner {\r\n\ttransform: translate3d(-100%,0,0);\r\n}\r\n\r\n.grid-wrap {\r\n\tdisplay: grid;\r\n\tmargin: 0 auto;\r\n\tgrid-template-columns: 100%;\r\n\tgrid-template-rows: 3rem 1fr;\r\n\tposition: relative;\r\n\tpadding: 13rem 5vw 2rem;\r\n\tpointer-events: none;\r\n}\r\n\r\n.gridback {\r\n\talign-self: start;\r\n\tgrid-area: 1 / 1 / 2 / 2;\r\n\tjustify-self: center;\r\n\tbackground: none;\r\n\tborder: 0;\r\n\tmargin: 0;\r\n\tpadding: 0;\r\n\tcolor: #fff;\r\n\topacity: 0;\r\n\tpointer-events: auto;\r\n}\r\n\r\n.page--preview .gridback {\r\n\tpointer-events: none;\r\n}\r\n\r\n.gridback:hover {\r\n\tcolor: var(--color-link-hover);\r\n}\r\n\r\n.gridback:focus {\r\n\toutline: none;\r\n}\r\n\r\n.grid {\r\n\tgrid-area: 2 / 1 / 3 / 2;\r\n\t--gridgap: 1vw;\r\n\t--gridwidth: 100%;\r\n\t--gridheight: 80vw;\r\n\tdisplay: grid;\r\n\twidth: var(--gridwidth);\r\n\theight: var(--gridheight);\r\n\tgrid-template-rows: repeat(10,calc(var(--gridheight) / 10 - var(--gridgap)));\r\n\tgrid-template-columns: repeat(10,calc(var(--gridwidth) / 10 - var(--gridgap)));\r\n\tgrid-gap: var(--gridgap);\r\n\talign-content: center;\r\n    \tjustify-content: center;\r\n}\r\n\r\n.grid__item-wrap {\r\n\tposition: relative;\r\n\twill-change: transform;\r\n}\r\n\r\n.grid__item {\r\n\topacity: 0;\r\n\tposition: relative;\r\n\twidth: 100%;\r\n\theight: 100%;\r\n\tbackground-repeat: no-repeat;\r\n\tbackground-position: 50% 50%;\r\n\tbackground-size: cover;\r\n\twill-change: transform;\r\n\t-webkit-filter: grayscale(0) contrast(1) brightness(1);\r\n\tfilter: grayscale(0) contrast(1) brightness(1);\r\n}\r\n\r\n.page--preview .grid__item {\r\n\t-webkit-filter: grayscale(0.5) contrast(0.4) brightness(1.5);\r\n\tfilter: grayscale(0.5) contrast(0.4) brightness(1.5);\r\n}\r\n\r\n/* Layout 1 */\r\n.grid--layout-1 .grid__item-wrap:first-child {grid-area: 3 / 1 / 8 / 4;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(2) {grid-area: 1 / 2 / 3 / 4;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(3) {grid-area: 5 / 4 / 8 / 8; }\r\n.grid--layout-1 .grid__item-wrap:nth-child(4) {grid-area: 2 / 7 / 5 / 11;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(5) {grid-area: 1 / 4 / 5 / 7;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(6) {grid-area: 8 / 5 / 11 / 2;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(7) {grid-area: 10 / 9 / 8 / 11;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(8) {grid-area: 5 / 8 / 8 / 10;}\r\n.grid--layout-1 .grid__item-wrap:nth-child(9) {grid-area: 8 / 5 / 11 / 9;}\r\n\r\n\r\n/* Layout 2 */\r\n.grid--layout-2 .grid__item-wrap:first-child { grid-area: 2 / 1 / 5 / 4; }\r\n.grid--layout-2 .grid__item-wrap:nth-child(2) { grid-area: 1 / 4 / 4 / 7; }\r\n.grid--layout-2 .grid__item-wrap:nth-child(3) {grid-area: 1 / 7 / 5 / 10;}\r\n.grid--layout-2 .grid__item-wrap:nth-child(4) {grid-area: 5 / 1 / 7 / 4;}\r\n.grid--layout-2 .grid__item-wrap:nth-child(5) {grid-area: 4 / 4 / 7 / 7;}\r\n.grid--layout-2 .grid__item-wrap:nth-child(6) {grid-area: 7 / 7 / 11 / 4;}\r\n.grid--layout-2 .grid__item-wrap:nth-child(7) {grid-area: 5 / 7 / 8 / 11;}\r\n.grid--layout-2 .grid__item-wrap:nth-child(8) {grid-area: 7 / 2 / 9 / 4;}\r\n\r\n/* Layout 3 */\r\n.grid--layout-3 .grid__item-wrap:first-child {grid-area: 1 / 2 / 3 / 5;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(2) {grid-area: 3 / 1 / 6 / 5;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(3) {grid-area: 1 / 5 / 5 / 8;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(4) {grid-area: 2 / 8 / 6 / 11;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(5) {grid-area: 5 / 5 / 8 / 8;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(6) {grid-area: 6 / 8 / 8 / 11;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(7) {grid-area: 6 / 2 / 8 / 5;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(8) {grid-area: 11 / 4 / 8 / 7;}\r\n.grid--layout-3 .grid__item-wrap:nth-child(9) {grid-area: 8 / 9 / 11 / 7;}\r\n\r\n/* Layout 4 */\r\n.grid--layout-4 .grid__item-wrap:first-child {grid-area: 2 / 1 / 4 / 4;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(2) {grid-area: 1 / 4 / 3 / 7;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(3) {grid-area: 3 / 4 / 5 / 7;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(4) {grid-area: 1 / 7 / 4 / 11;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(5) {grid-area: 4 / 2 / 7 / 4;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(6) {grid-area: 5 / 7 / 8 / 4;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(7) {grid-area: 4 / 7 / 8 / 11;}\r\n.grid--layout-4 .grid__item-wrap:nth-child(8) {grid-area: 8 / 9 / 11 / 4;}\r\n\r\n/* Layout 5 */\r\n.grid--layout-5 .grid__item-wrap:first-child {grid-area: 2 / 1 / 5 / 4;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(2) {grid-area: 1 / 4 / 5 / 7;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(3) {grid-area: 5 / 2 / 7 / 5;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(4) {grid-area: 1 / 7 / 4 / 11;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(5) {grid-area: 5 / 7 / 7 / 5;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(6) {grid-area: 7 / 5 / 10 / 1;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(7) {grid-area: 4 / 7 / 7 / 9;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(8) {grid-area: 4 / 9 / 9 / 11;}\r\n.grid--layout-5 .grid__item-wrap:nth-child(9) {grid-area: 7 / 5 / 11 / 9;}\r\n\r\n.page--preview {\r\n\tposition: relative;\r\n\toverflow: hidden;\r\n\theight: 100vh;\r\n\tpointer-events: none;\r\n}\r\n\r\n@media screen and (min-width: 53em) {\r\n\t.frame {\r\n\t\tposition: fixed;\r\n\t\ttext-align: left;\r\n\t\tz-index: 10000;\r\n\t\ttop: 0;\r\n\t\tleft: 0;\r\n\t\tdisplay: grid;\r\n\t\talign-content: space-between;\r\n\t\twidth: 100%;\r\n\t\tmax-width: none;\r\n\t\theight: 100vh;\r\n\t\tpadding: 2.5rem 3rem;\r\n\t\tgrid-template-columns: 20rem 1fr 1fr;\r\n\t\tgrid-template-rows: auto auto auto;\r\n\t\tgrid-template-areas: 'title links pagetitle'\r\n\t\t\t\t\t\t\t'... ... ...'\r\n\t\t\t\t\t\t\t'... ... social';\r\n\t}\r\n\t.frame__pagetitle {\r\n\t\tgrid-area: pagetitle;\r\n\t\tmargin: 0;\r\n\t}\r\n\t.frame__title-wrap {\r\n\t\tgrid-area: title;\r\n\t\tdisplay: flex;\r\n\t}\r\n\t.frame__title {\r\n\t\tmargin: 0;\r\n\t}\r\n\t.frame__tagline {\r\n\t\tposition: relative;\r\n\t\tmargin: 0 0 0 1rem;\r\n\t\tpadding: 0 0 0 1rem;\r\n\t}\r\n\t.frame__social {\r\n\t\tmargin: 0;\r\n\t\tgrid-area: social;\r\n\t\tjustify-self: end;\r\n\t}\r\n\t.frame__links {\r\n\t\tgrid-area: links;\r\n\t\tpadding: 0;\r\n\t\tjustify-self: start;\r\n\t}\r\n\t.grid-wrap {\r\n\t\tgrid-template-rows: 1.25rem 1fr;\r\n\t\tgrid-gap: 3rem;\r\n\t\tpadding: 2.5rem 3rem;\r\n\t}\r\n\t.grid {\r\n\t\tpadding: 0 10vw;\r\n\t}\r\n\t.gridback {\r\n\t\tjustify-self: end;\r\n\t}\r\n}\r\n\r\n@media (any-pointer: fine) {\r\n\t.cursor {\r\n\t\tdisplay: block;\r\n\t}\r\n\t.cursor__inner {\r\n\t\tz-index: 9999;\r\n\t\tpointer-events: none;\r\n\t\tposition: absolute;\r\n\t\ttop: 0;\r\n\t\tleft: 0;\r\n\t\tmix-blend-mode: difference;\r\n\t\tborder-radius: 50%;\r\n\t}\r\n\t.cursor__side {\r\n\t\tposition: absolute;\r\n\t\ttop: 50%;\r\n\t\twidth: 5px;\r\n\t\theight: 1px;\r\n\t\tbackground: #de6565;\r\n\t\topacity: 0;\r\n\t}\r\n\t.cursor__side--left {\r\n\t\tright: calc(100% + 5px);\r\n\t}\r\n\t.cursor__side--right {\r\n\t\tleft: calc(100% + 5px);\r\n\t}\r\n\t.cursor__inner--circle {\r\n\t\twidth: 25px;\r\n\t\theight: 25px;\r\n\t\tborder: 1px solid #de6565;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" class=\"no-js\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t\t<title>Draggable Menu with Grid Previews | Codrops</title>\n\t\t<meta name=\"description\" content=\"A draggable menu that shows a thumbnail preview of an image grid\" />\n\t\t<meta name=\"keywords\" content=\"draggable, menu, navigation, thumbnails, grid, javascript, gsap, web design, layout\" />\n\t\t<meta name=\"author\" content=\"Codrops\" />\n\t\t<link rel=\"shortcut icon\" href=\"favicon.ico\">\n\t\t<link rel=\"stylesheet\" href=\"https://use.typekit.net/crf4rue.css\">\n\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"css/base.css\" />\n\t\t<script src=\"https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js\"></script>\n\t\t<script>document.documentElement.className=\"js\";var supportsCssVars=function(){var e,t=document.createElement(\"style\");return t.innerHTML=\"root: { --tmp-var: bold; }\",document.head.appendChild(t),e=!!(window.CSS&&window.CSS.supports&&window.CSS.supports(\"font-weight\",\"var(--tmp-var)\")),t.parentNode.removeChild(t),e};supportsCssVars()||alert(\"Please view this demo in a modern browser that supports CSS Variables.\");</script>\n\t</head>\n\t<body class=\"loading\">\n\t\t<main>\n\t\t\t<div class=\"frame\">\n\t\t\t\t<div class=\"frame__pagetitle\">Draggable Menu with Grid Previews</div>\n\t\t\t\t<div class=\"frame__title-wrap\">\n\t\t\t\t\t<h1 class=\"frame__title\">Yuri Shevchenko</h1>\n\t\t\t\t\t<p class=\"frame__tagline\">2008 &mdash; 2019</a></p>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"frame__links\">\n\t\t\t\t\t<a href=\"https://tympanus.net/Development/DraggableImageStrip/\">Previous Demo</a>\n\t\t\t\t\t<a href=\"https://tympanus.net/codrops/?p=40926\">Article</a>\n\t\t\t\t\t<a href=\"https://github.com/codrops/DraggableMenu/\">GitHub</a>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"frame__social\">\n\t\t\t\t\t<a href=\"#\" aria-label=\"Link to Yuri's Behance profile\"><img src=\"img/behance.svg\" class=\"frame__social-behance\" alt=\"Behance Logo\"/></a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div class=\"page page--preview\">\n\t\t\t\t<div class=\"grid-wrap\">\n\t\t\t\t\t<div class=\"grid grid--layout-1\">\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/1.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/2.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/3.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/4.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/5.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/6.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/7.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/8.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/9.jpg)\"></div></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"grid grid--layout-2\">\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/10.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/11.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/12.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/13.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/14.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/15.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/16.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/17.jpg)\"></div></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"grid grid--layout-3\">\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/18.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/19.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/20.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/21.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/22.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/23.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/24.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/42.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/43.jpg)\"></div></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"grid grid--layout-4\">\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/25.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/26.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/27.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/28.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/29.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/30.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/31.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/32.jpg)\"></div></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"grid grid--layout-5\">\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/33.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/34.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/35.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/36.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/37.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/38.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/39.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/40.jpg)\"></div></div>\n\t\t\t\t\t\t<div class=\"grid__item-wrap\"><div class=\"grid__item\" style=\"background-image: url(img/41.jpg)\"></div></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<button class=\"gridback\"><svg width=\"27px\" height=\"15px\" viewBox=\"0 0 27 15\"><path d=\"M1.469 6.75l-.719.719 7.938 6.937.718-.719L1.47 6.75zM8.594.531L.75 7.375l.688.688L9.28 1.218 8.594.53zM1.406 6.938v1h24.75v-1H1.406z\" fill=\"#de6565\"/></svg></button>\n\t\t\t\t</div><!-- /grid-wrap -->\n\t\t\t</div><!-- /page -->\n\t\t\t<div class=\"menu-wrap\">\n\t\t\t\t<div class=\"menu-draggable\"></div>\n\t\t\t\t<nav class=\"menu\">\n\t\t\t\t\t<div class=\"menu__item\">\n\t\t\t\t\t\t<a class=\"menu__item-link\">Mezcala</a>\n\t\t\t\t\t\t<a class=\"menu__item-explore\">explore</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"menu__item\">\n\t\t\t\t\t\t<a class=\"menu__item-link\">Caricia</a>\n\t\t\t\t\t\t<a class=\"menu__item-explore\">explore</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"menu__item\">\n\t\t\t\t\t\t<a class=\"menu__item-link\">Esquirla</a>\n\t\t\t\t\t\t<a class=\"menu__item-explore\">explore</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"menu__item\">\n\t\t\t\t\t\t<a class=\"menu__item-link\">Sangre</a>\n\t\t\t\t\t\t<a class=\"menu__item-explore\">explore</a>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"menu__item\">\n\t\t\t\t\t\t<a class=\"menu__item-link\">Petricor</a>\n\t\t\t\t\t\t<a class=\"menu__item-explore\">explore</a>\n\t\t\t\t\t</div>\n\t\t\t\t</nav><!--menu-->\n\t\t\t</div><!--/menu-wrap-->\n\t\t</main>\n\t\t<div class=\"cursor\">\n\t\t\t<div class=\"cursor__inner cursor__inner--circle\">\n\t\t\t\t<div class=\"cursor__side cursor__side--left\"></div>\n\t\t\t\t<div class=\"cursor__side cursor__side--right\"></div>\n\t\t\t</div>\n\t\t</div>\n\t\t<script src=\"js/imagesloaded.pkgd.min.js\"></script>\n\t\t<script src=\"js/charming.min.js\"></script>\n\t\t<script src=\"js/TweenMax.min.js\"></script>\n\t\t<script src=\"js/draggabilly.pkgd.min.js\"></script>\n\t\t<script src=\"js/demo.js\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "js/demo.js",
    "content": "/**\n* demo.js\n* http://www.codrops.com\n*\n* Licensed under the MIT license.\n* http://www.opensource.org/licenses/mit-license.php\n* \n* Copyright 2019, Codrops\n* http://www.codrops.com\n*/\n{\n    // Helper functions\n    const MathUtils = {\n        lineEq: (y2, y1, x2, x1, currentVal) => {\n            // y = mx + b \n            var m = (y2 - y1) / (x2 - x1), b = y1 - m * x1;\n            return m * currentVal + b;\n        },\n        lerp: (a, b, n) => (1 - n) * a + n * b,\n        getRandomFloat: (min, max) => (Math.random() * (max - min) + min).toFixed(2)\n    };\n\n    // Gets the mouse position\n    const getMousePos = (e) => {\n        let posx = 0;\n        let posy = 0;\n        if (!e) e = window.event;\n        if (e.pageX || e.pageY) {\n            posx = e.pageX;\n            posy = e.pageY;\n        }\n        else if (e.clientX || e.clientY)    {\n            posx = e.clientX + body.scrollLeft + document.documentElement.scrollLeft;\n            posy = e.clientY + body.scrollTop + document.documentElement.scrollTop;\n        }\n        return { x : posx, y : posy }\n    };\n\n    // https://pawelgrzybek.com/page-scroll-in-vanilla-javascript/\n    function scrollIt(destination, duration = 200, easing = 'linear', callback) {\n        const easings = {\n            linear(t) {\n                return t;\n            },\n            easeOutQuad(t) {\n                return t * (2 - t);\n            },\n        };\n      \n        const start = window.pageYOffset;\n        const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();\n\n        const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);\n        const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight;\n        const destinationOffset = typeof destination === 'number' ? destination : destination.offsetTop;\n        const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);\n      \n        if ('requestAnimationFrame' in window === false) {\n            window.scroll(0, destinationOffsetToScroll);\n            if (callback) {\n                callback();\n            }\n            return;\n        }\n      \n        function scroll() {\n            const now = 'now' in window.performance ? performance.now() : new Date().getTime();\n            const time = Math.min(1, ((now - startTime) / duration));\n            const timeFunction = easings[easing](time);\n            window.scroll(0, Math.abs(Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start)));\n            if (window.pageYOffset === destinationOffsetToScroll) {\n                if (callback) {\n                    callback();\n                }\n                return;\n            }\n\n            requestAnimationFrame(scroll);\n        }\n      \n        scroll();\n    }\n\n    // Calculate the viewport size\n    let winsize;\n    const calcWinsize = () => winsize = {width: window.innerWidth, height: window.innerHeight};\n    calcWinsize();\n    window.addEventListener('resize', calcWinsize);\n\n    // Track the mouse position\n    let mousepos = {x: winsize.width/2, y: winsize.height/2};\n    window.addEventListener('mousemove', ev => mousepos = getMousePos(ev));\n\n    // Custom cursor\n    class Cursor {\n        constructor(el) {\n            this.DOM = {el: el};\n            this.DOM.circle = this.DOM.el.querySelector('.cursor__inner--circle');\n            this.DOM.arrows = {\n                right: this.DOM.el.querySelector('.cursor__side--right'),\n                left: this.DOM.el.querySelector('.cursor__side--left')\n            };\n            this.bounds = this.DOM.circle.getBoundingClientRect();\n\n            this.renderedStyles = {\n                tx: {previous: 0, current: 0, amt: 0.2},\n                ty: {previous: 0, current: 0, amt: 0.2},\n                scale: {previous: 1, current: 1, amt: 0.2}\n            };\n            requestAnimationFrame(() => this.render());\n        }\n        render() {\n            this.renderedStyles['tx'].current = mousepos.x - this.bounds.width/2;\n            this.renderedStyles['ty'].current = mousepos.y - this.bounds.height/2;\n\n            for (const key in this.renderedStyles ) {\n                this.renderedStyles[key].previous = MathUtils.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);\n            }\n                        \n            this.DOM.circle.style.transform = `translateX(${(this.renderedStyles['tx'].previous)}px) translateY(${this.renderedStyles['ty'].previous}px) scale(${this.renderedStyles['scale'].previous})`;\n            requestAnimationFrame(() => this.render());\n        }\n        enter() {\n            this.renderedStyles['scale'].current = 1.9;\n        }\n        leave() {\n            this.renderedStyles['scale'].current = 1;\n        }\n        click() {\n            this.renderedStyles['scale'].previous = 0.4;\n        }\n        showArrows() {\n            TweenMax.to(Object.values(this.DOM.arrows), ANIMATION_SETTINGS.cursor.duration, {\n                ease: ANIMATION_SETTINGS.cursor.ease,\n                startAt: {x: i => i ? 10 : -10 },\n                opacity: 1,\n                x: 0\n            });\n        }\n        hideArrows() {\n            TweenMax.to(Object.values(this.DOM.arrows), ANIMATION_SETTINGS.cursor.duration, {\n                ease: ANIMATION_SETTINGS.cursor.ease,\n                x: i => i ? 10 : -10,\n                opacity: 0\n            });\n        }\n    }\n\n    // Images Grid\n    class ImageGrid {\n        constructor(el) {\n            this.DOM = {el: el};\n            this.DOM.imageWrap = [...this.DOM.el.querySelectorAll('.grid__item-wrap')];\n            this.itemsTotal = this.DOM.imageWrap.length;\n            this.DOM.images = [...this.DOM.el.querySelectorAll('.grid__item')];\n            // Spread the grid items\n            this.spread();\n        }\n        // Spreads the grid items by randomly positioning them and scaling them down\n        spread(animate = false) {\n            return new Promise((resolve, reject) => {\n                let animateCount = 0;\n                const gridHeight = this.DOM.el.scrollHeight;\n                const gridTop = this.DOM.el.offsetTop;\n                this.DOM.imageWrap.forEach((item) => {\n                    const rect = item.getBoundingClientRect();\n                    \n                    // Item´s center point\n                    const center = {x: rect.left+rect.width/2, y: rect.top+rect.height/2};\n                    // Calculate the item´s quadrant in the viewport\n                    const quadrant = center.x >= winsize.width/2 ?\n                                        center.y <= gridHeight/2 + gridTop ? 1 : 4 :\n                                        center.y <= gridHeight/2 + gridTop ? 2 : 3;\n                    \n                    // Now calculate how much to translate the item\n                    // The positions will be random but only in the area of the item´s quadrant\n                    // Also, consider a margin so the item does not stay completely out of the viewport or its quadrant\n                    const margins = {x: winsize.width*.02, y: winsize.height*.04}\n                    const tx = quadrant === 1 || quadrant === 4 ? \n                            MathUtils.getRandomFloat(-1*center.x + winsize.width/2 + margins.x*4, winsize.width - center.x - margins.x) :\n                            MathUtils.getRandomFloat(-1*center.x + margins.x, winsize.width/2 - center.x - margins.x*4);\n                    const ty = quadrant === 1 || quadrant === 2 ?\n                            MathUtils.getRandomFloat(-1*center.y + margins.y, winsize.height/2 - center.y - margins.y*4) :\n                            MathUtils.getRandomFloat(-1*center.y + winsize.height/2 + margins.y*4, winsize.height - center.y - margins.y);\n\n                    // Save the current translation\n                    item.dataset.ctx = tx;\n                    item.dataset.cty = ty;\n\n                    if ( animate ) {\n                        TweenMax.to(item, ANIMATION_SETTINGS.grid.duration, {\n                            ease: ANIMATION_SETTINGS.grid.ease,\n                            x: tx,\n                            y: ty,\n                            scale: 0.35,\n                            onComplete: () => {\n                                ++animateCount;\n                                if ( animateCount === this.itemsTotal ) {\n                                    resolve();\n                                }\n                            }\n                        });\n                    }\n                    else {\n                        TweenMax.set(item, {\n                            x: tx,\n                            y: ty,\n                            scale: 0.35\n                        });\n                        resolve();\n                    }\n                });\n            });\n        }\n        // Resets the items to the original position (forming again the original grid)\n        collapse() {\n            return new Promise((resolve, reject) => {\n                TweenMax.to(this.DOM.imageWrap, ANIMATION_SETTINGS.grid.duration, {\n                    ease: ANIMATION_SETTINGS.grid.ease,\n                    x: 0,\n                    y: 0,\n                    scale: 1.01,\n                    onComplete: resolve\n                });\n            });\n        }\n        showImages() {\n            TweenMax.set(this.DOM.images, {opacity: 1});\n        }\n    }\n\n    // A menu item\n    class MenuItem {\n        constructor(el, imageGrid) {\n            // The main wrapper\n            this.DOM = {el: el};\n            // The inner link (.menu__item-link)\n            this.DOM.link = this.DOM.el.querySelector('.menu__item-link');\n            // The explore link\n            this.DOM.explore = this.DOM.el.querySelector('.menu__item-explore');\n            // We will need the size and position for the calculations needed to drag/translate the menu\n            this.rect = this.DOM.el.getBoundingClientRect();\n            // The images grid for this menu item\n            this.imageGrid = imageGrid;\n            // As we drag, the letters will switch from only stroke to filled and vice versa\n            // We need to split the letters into spans and create the necessary structure (we will have two spans per letter, one for the stroke version and one for the filled)\n            charming(this.DOM.link, {classPrefix: false});\n            const linkInner = [...this.DOM.link.querySelectorAll('span')];\n            linkInner.forEach((span) => {\n                const stroke = span.cloneNode(true);\n                span.classList.add('letter__inner','letter__inner--filled');\n                stroke.classList.add('letter__inner','letter__inner--stroke');\n                this.DOM.link.insertBefore(stroke, span.nextSibling);\n                const letter = document.createElement('span');\n                letter.classList = 'letter';\n                letter.appendChild(span);\n                letter.appendChild(stroke);\n                this.DOM.link.appendChild(letter);\n            });\n            this.letters = [...this.DOM.link.querySelectorAll('.letter__inner')];\n            // Need to recalculate size and position on window resize\n            window.addEventListener('resize', () => this.rect = this.DOM.el.getBoundingClientRect());\n        }\n        setCurrent() {\n            this.DOM.el.classList.add('menu__item--current');\n            return this;\n        }\n        unsetCurrent() {\n            this.DOM.el.classList.remove('menu__item--current');\n        }\n        isCurrent() {\n            return this.DOM.el.classList.contains('menu__item--current');\n        }\n        // Show/Hide the explore link \n        showExplore() {\n            return this.toggleExplorer('show');\n        }\n        hideExplore() {\n            return this.toggleExplorer('hide');\n        }\n        toggleExplorer(action = 'show') {\n            return new Promise((resolve, reject) => {\n                TweenMax.to(this.DOM.explore, ANIMATION_SETTINGS.explore.duration, {\n                    ease: ANIMATION_SETTINGS.explore.ease,\n                    startAt: action === 'hide' ? null : {scale: 0.5},\n                    opacity: action === 'hide' ? 0 : 1,\n                    scale: action === 'hide' ? 0.8 : 1,\n                    onComplete: resolve\n                });\n            });\n        }\n        // Show/Hide the letters\n        show() {\n            return this.toggle('show');\n        }\n        hide() {\n            return this.toggle('hide');\n        }\n        toggle(action = 'show') {\n            return new Promise((resolve, reject) => {\n                const tx = action === 'hide' ? this.isCurrent() ? '-200%' : '100%' : this.isCurrent() ? '-100%' : '0%';\n                TweenMax.to(this.letters, ANIMATION_SETTINGS.allMenuLettersToggle.duration, {\n                    ease: ANIMATION_SETTINGS.allMenuLettersToggle.ease,\n                    x: tx,\n                    onComplete: resolve\n                });\n            });\n        }\n    }\n\n    // The menu\n    class Menu {\n        constructor(el) {\n            // The menu wrap (.menu-wrap)\n            this.DOM = {el: el};\n            // The menu element\n            this.DOM.menu = this.DOM.el.querySelector('.menu');\n            // The draggable container\n            this.DOM.draggable = this.DOM.el.querySelector('.menu-draggable');\n            // Content wrap\n            this.DOM.pagePreview = document.querySelector('.page--preview');\n            // The ctrl that closes the grid view and shows back the menu\n            this.DOM.backToMenuCtrl = this.DOM.pagePreview.querySelector('.gridback');\n            \n            // The image grids (one per menu item)\n            this.imageGrids = [];\n            [...this.DOM.pagePreview.querySelectorAll('.grid')].forEach(item => this.imageGrids.push(new ImageGrid(item)));\n            // MenuItem instances\n            this.menuItems = [];\n            [...this.DOM.menu.querySelectorAll('.menu__item')].forEach((item, position) => this.menuItems.push(new MenuItem(item, this.imageGrids[position])));\n            // Total number of menu items\n            this.menuItemsTotal = this.menuItems.length;\n            // Index of the current menuItem\n            this.current = 0;\n            // Set the first menu item to current and show its explore link\n            this.menuItems[this.current].setCurrent().showExplore();\n            // Show the first grid items\n            this.menuItems[this.current].imageGrid.showImages();\n            // Initialize the Draggabilly (on the x axis)\n            this.draggie = new Draggabilly(this.DOM.draggable, { axis: 'x' });\n            // The current amount (in pixels) that was dragged\n            this.dragPosition = 0;\n            // Minimum amount to drag in order to navigate to the next/previous menu item\n            this.minDrag = winsize.width*.04;\n            // Set the menu initial position\n            this.layout();\n            // The following are the values that need to be updated inside the render (rAF) function: \n            // - the menu translation value \n            // - the letters/spans (stroke and filled) translation values\n            // - and the grid images opacity and transform values\n            // The \"current\" and the \"previous\" hold the values to interpolate (\"current\" being the one we want to get to) and the \"amt\" is the amount to interpolate\n            this.renderedStyles = {\n                menuTranslation: {previous: this.dragPosition + this.initTx, current: this.dragPosition + this.initTx, amt: 0.1},\n                letterTranslation: {previous: 0, current: 0, amt: 0.1},\n                imgOpacity: {previous: 1, current: 1, amt: 0.1},\n                imgScaleX: {previous: 1, current: 1, amt: 0.06},\n                imgScaleY: {previous: 1, current: 1, amt: 0.06},\n                imgTranslation: {previous: 0, current: 0, amt: 0.1}\n            };\n            // Start the rAF loop to render the menu and letters positions\n            this.renderId = requestAnimationFrame(() => this.render());\n            // Initialize/Bind some events\n            this.initEvents();\n        }\n        layout() {\n            // Set the menu position/translation so that the first menu item is the current one thus positioned at the center\n            // We need to save these values for later calculations when translating the menu\n            this.initTx = this.currentPosition = winsize.width/2 - this.menuItems[this.current].rect.width/2;\n            TweenMax.set(this.DOM.menu, {x: this.initTx});\n        }\n        // Window resize\n        resize() {\n            this.minDrag = winsize.width*.04;\n            // Update position\n            this.currentPosition = winsize.width/2 - this.menuItems[this.current].DOM.el.offsetLeft - this.menuItems[this.current].rect.width/2;\n            this.renderedStyles.menuTranslation.current = this.renderedStyles.menuTranslation.previous = this.currentPosition;\n        }\n        isDragging() {\n            // dragDirection is only set when we drag the menu, so this can be used to checked if we are currently dragging\n            return this.dragDirection != undefined && this.dragDirection != '';\n        }\n        render() {\n            this.renderId = undefined;\n\n            // Apply the lerp function to the updated values\n            for (const key in this.renderedStyles ) {\n                this.renderedStyles[key].previous = MathUtils.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);\n            }\n            \n            // Translate the menu\n            TweenMax.set(this.DOM.menu, {x: this.renderedStyles.menuTranslation.previous});\n\n            // Switch the filled spans with stroke ones and vice versa\n            // Also update the grid images\n            if ( this.isDragging() && this.currentItem && this.upcomingItem ) {\n                let tx = this.renderedStyles.letterTranslation.previous;\n                TweenMax.set(this.currentItem.letters, {x: this.dragDirection === 'left' ? -1*tx-100 + '%' : tx-100 + '%'});\n                TweenMax.set(this.upcomingItem.letters, {x: this.dragDirection === 'left' ? tx + '%' : -1*tx + '%'});\n\n                TweenMax.set(this.currentItem.imageGrid.DOM.images, {\n                    transformOrigin: this.dragDirection === 'left' ? '100% 50%' : '0% 50%',\n                    opacity: this.renderedStyles.imgOpacity.previous,\n                    scaleX: this.renderedStyles.imgScaleX.previous,\n                    scaleY: this.renderedStyles.imgScaleY.previous,\n                    x: this.dragDirection === 'left' ? -1*this.renderedStyles.imgTranslation.previous + '%' : this.renderedStyles.imgTranslation.previous + '%'\n                });\n                TweenMax.set(this.upcomingItem.imageGrid.DOM.images, {\n                    transformOrigin: this.dragDirection === 'left' ? '0% 50%' : '100% 50%',\n                    opacity: Math.abs(1-this.renderedStyles.imgOpacity.previous),\n                    scaleX: 3-this.renderedStyles.imgScaleX.previous,\n                    scaleY: 1.8-this.renderedStyles.imgScaleY.previous,\n                    x: this.dragDirection === 'left' ? 150 - this.renderedStyles.imgTranslation.previous + '%' : -1*(150 - this.renderedStyles.imgTranslation.previous) + '%'\n                });\n            }\n\n            if ( !this.renderId ) {\n                this.renderId = requestAnimationFrame(() => this.render());\n            }\n        }\n        initEvents() {\n            this.onPointerDown = () => {\n                // Scale up the cursor\n                cursor.renderedStyles['scale'].current = 1.5;\n                // And show the \"drag mode\" arrows\n                cursor.showArrows();\n            };\n\n            this.onDragStart = () => {\n                if ( this.isAnimating ) return;\n                // Reset the drag direction value\n                this.dragDirection = '';\n            };\n\n            // Save the previous moveVector obj that Draggability provides for every drag move\n            // We need this to track the current direction of dragging in order to compare it later with the initial intended direction\n            // Like so we know if the menu should navigate to the next/previous item or if the navigation needs to be cancelled\n            this.cachedVectorMovement = {x:0,y:0};\n            this.onDragMove = (event, pointer, moveVector) => {\n                // Update the mouse position\n                mousepos = getMousePos(event);\n\n                // Return if theres an active animation\n                if ( this.isAnimating ) return;\n                \n                // Track the current direction of the drag\n                if ( moveVector.x != this.cachedVectorMovement.x ) {\n                    this.currentDirection = moveVector.x > this.cachedVectorMovement.x ? 'right' : 'left';\n                    this.cachedVectorMovement = moveVector;\n                }\n\n                if ( this.dragDirection === '' ) {\n                    // Hide the explorer link\n                    this.menuItems[this.current].hideExplore();\n\n                    // The initial intended direction\n                    this.dragDirection = moveVector.x > 0 ? 'right' : 'left';\n                    \n                    // We need to calculate the amount to move the menu as we drag from one point of the screen to another.\n                    // If we are switching between two menu items then this value is the distance from the center of the current menu item to the center of the next or previous menuItem (depending on the dragging direction)\n                    // otherwise it will be the same as this.minDrag so that when we stop dragging the navigation gets cancelled\n\n                    // Boundary cases\n                    if ( this.dragDirection === 'right' && this.current === 0 || this.dragDirection === 'left' && this.current === this.menuItemsTotal - 1 ) {\n                        this.amountToMove = this.minDrag;\n                    }\n                    // else move to the next/previous menuItem\n                    else {\n                        this.upcomingIdx = this.dragDirection === 'left' ? this.current+1 : this.current-1;\n                        this.currentItem = this.menuItems[this.current];\n                        this.upcomingItem = this.menuItems[this.upcomingIdx];\n                        this.amountToMove = Math.abs((this.currentItem.rect.left + this.currentItem.rect.width/2) - (this.upcomingItem.rect.left + this.upcomingItem.rect.width/2));\n                    }\n                }\n                // Update the dragPosition:\n                // We need to map the draggable movement ([0,winsize.width]) to the menu movement ([0,amountToMove])\n                this.dragPosition = MathUtils.lineEq(this.amountToMove, 0, winsize.width, 0, this.draggie.position.x);\n                // Finally update both the menu translation, letters translation and grid images (rAF render function)\n                this.renderedStyles.menuTranslation.current = this.dragPosition + this.currentPosition;\n                this.renderedStyles.letterTranslation.current = MathUtils.lineEq(100, 0, winsize.width, 0, this.dragDirection === 'left' ? Math.min(this.draggie.position.x, 0) : Math.max(this.draggie.position.x, 0));\n                this.renderedStyles.imgOpacity.current = MathUtils.lineEq(0, 1, winsize.width, 0, this.dragDirection === 'left' ? Math.abs(Math.min(this.draggie.position.x, 0)) : Math.abs(Math.max(this.draggie.position.x, 0)));\n                this.renderedStyles.imgScaleX.current = MathUtils.lineEq(2, 1, winsize.width, 0, this.dragDirection === 'left' ? Math.abs(Math.min(this.draggie.position.x, 0)) : Math.abs(Math.max(this.draggie.position.x, 0)));\n                this.renderedStyles.imgScaleY.current = MathUtils.lineEq(0.8, 1, winsize.width, 0, this.dragDirection === 'left' ? Math.abs(Math.min(this.draggie.position.x, 0)) : Math.abs(Math.max(this.draggie.position.x, 0)));\n                this.renderedStyles.imgTranslation.current = MathUtils.lineEq(150, 0, winsize.width, 0, this.dragDirection === 'left' ? Math.abs(Math.min(this.draggie.position.x, 0)) : Math.abs(Math.max(this.draggie.position.x, 0)));\n            };\n\n            this.onPointerUp = () => {\n                // Scale down the cursor (reset)\n                cursor.renderedStyles['scale'].current = 1;\n                // And hide the \"drag mode\" arrows\n                cursor.hideArrows();\n            };\n\n            this.onDragEnd = () => {\n                if ( !this.isAnimating ) {\n                    this.isAnimating = true;\n\n                    // Cancel the render function (rAF) \n                    if ( this.renderId ) {\n                        window.cancelAnimationFrame(this.renderId);\n                        this.renderId = undefined;\n                    }\n\n                    // Cancel the navigation:\n                    // Either it didn´t drag enough (<= minDrag) or the drag direction changed to the opposite one, meaning the user stepped back from navigating\n                    if ( Math.abs(this.dragPosition) <= this.minDrag || this.dragDirection !== this.currentDirection ) {\n                        // Show the explore link\n                        this.menuItems[this.current].showExplore();\n\n                        // Reset the rAF updated values\n                        this.renderedStyles.menuTranslation.current = this.renderedStyles.menuTranslation.previous = this.currentPosition;\n                        this.renderedStyles.letterTranslation.current = this.renderedStyles.letterTranslation.previous = 0;\n                        this.renderedStyles.imgOpacity.current = this.renderedStyles.imgOpacity.previous = 1;\n                        this.renderedStyles.imgScaleX.current = this.renderedStyles.imgScaleX.previous = 1;\n                        this.renderedStyles.imgScaleY.current = this.renderedStyles.imgScaleY.previous = 1;\n                        this.renderedStyles.imgTranslation.current = this.renderedStyles.imgTranslation.previous = 0;\n\n                        const tl = new TimelineMax({\n                            onComplete: () => {\n                                // Restart the rAF loop\n                                this.renderId = requestAnimationFrame(() => this.render());\n                                // Reset values..\n                                this.currentItem = undefined;\n                                this.upcomingItem = undefined;\n                                // Able to drag and animate again\n                                this.isAnimating = false;\n                            }\n                        })\n                        // Animate the menu back to the previous position\n                        .to(this.DOM.menu, ANIMATION_SETTINGS.menu.duration, {\n                            ease: ANIMATION_SETTINGS.menu.ease,\n                            x: this.currentPosition\n                        }, 0);\n\n                        // Reset the letters translations and grid images\n                        if ( this.currentItem && this.upcomingItem ) {\n                            tl\n                            .to(this.currentItem.letters, ANIMATION_SETTINGS.letters.duration, {\n                                ease: ANIMATION_SETTINGS.letters.ease,\n                                x: '-100%'\n                            }, 0)\n                            .to(this.upcomingItem.letters, ANIMATION_SETTINGS.letters.duration, {\n                                ease: ANIMATION_SETTINGS.letters.ease,\n                                x: '0%'\n                            }, 0)\n                            .to(this.currentItem.imageGrid.DOM.images, ANIMATION_SETTINGS.images.duration, {\n                                ease: ANIMATION_SETTINGS.images.ease,\n                                opacity: 1,\n                                scaleX: 1,\n                                scaleY: 1,\n                                x: '0%'\n                            }, 0)\n                            .to(this.upcomingItem.imageGrid.DOM.images, ANIMATION_SETTINGS.images.duration, {\n                                ease: ANIMATION_SETTINGS.images.ease,\n                                opacity: 0,\n                                scaleX: 2,\n                                scaleY: 0.8,\n                                x: this.dragDirection === 'left' ? '150%' : '-150%'\n                            }, 0);\n                        }\n                    }\n                    // Move to the next/previous menu item\n                    else {\n                        // Show the explore link\n                        this.menuItems[this.upcomingIdx].showExplore();\n\n                        // Set the updated menu translation value\n                        this.currentPosition \n                                = this.renderedStyles.menuTranslation.current = this.renderedStyles.menuTranslation.previous \n                                = this.dragDirection === 'left' ? \n                                    this.currentPosition - this.amountToMove : \n                                    this.currentPosition + this.amountToMove;\n                        \n                        // Reset letters translation value\n                        this.renderedStyles.letterTranslation.current = this.renderedStyles.letterTranslation.previous = 0;\n\n                        // Reset grid images values\n                        this.renderedStyles.imgOpacity.current = this.renderedStyles.imgOpacity.previous = 1;\n                        this.renderedStyles.imgScaleX.current = this.renderedStyles.imgScaleX.previous = 1;\n                        this.renderedStyles.imgScaleY.current = this.renderedStyles.imgScaleY.previous = 1;\n                        this.renderedStyles.imgTranslation.current = this.renderedStyles.imgTranslation.previous = 0;\n\n                        const tl = new TimelineMax({\n                            onComplete: () => {\n                                // Restart the rAF loop\n                                this.renderId = requestAnimationFrame(() => this.render());\n                                // Update the menu item current state\n                                this.currentItem.unsetCurrent();\n                                this.upcomingItem.setCurrent();\n                                // Update the current item index value\n                                this.current = this.upcomingIdx;\n                                // Reset values.. \n                                this.currentItem = undefined;\n                                this.upcomingItem = undefined;\n                                // Able to drag and animate again\n                                this.isAnimating = false;\n                            }\n                        })\n                        // Animate the menu translation\n                        .to(this.DOM.menu, ANIMATION_SETTINGS.menu.duration, {\n                            ease: ANIMATION_SETTINGS.menu.ease,\n                            x: this.currentPosition\n                        }, 0)\n                        // Animate the letters (current item gets stroke letters while the previous current item gets filled, thus the translation needs to be set differently for the current and upcoming item)\n                        .to(this.currentItem.letters, ANIMATION_SETTINGS.letters.duration, {\n                            ease: ANIMATION_SETTINGS.letters.ease,\n                            x: '0%'\n                        }, 0)\n                        .to(this.upcomingItem.letters, ANIMATION_SETTINGS.letters.duration, {\n                            ease: ANIMATION_SETTINGS.letters.ease,\n                            x: '-100%'\n                        }, 0)\n                        // And animate the grid images\n                        .to(this.currentItem.imageGrid.DOM.images, ANIMATION_SETTINGS.images.duration, {\n                            ease: ANIMATION_SETTINGS.images.ease,\n                            opacity: 0,\n                            scaleX: 2,\n                            scaleY: 0.8,\n                            x: this.dragDirection === 'left' ? '-150%' : '150%'\n                        }, 0)\n                        .to(this.upcomingItem.imageGrid.DOM.images, ANIMATION_SETTINGS.images.duration, {\n                            ease: ANIMATION_SETTINGS.images.ease,\n                            opacity: 1,\n                            scaleX: 1,\n                            scaleY: 1,\n                            x: '0%'\n                        }, 0);\n                    }\n                }\n\n                // Reset the drag position value\n                this.dragPosition = 0;\n                this.draggie.setPosition(this.dragPosition, this.draggie.position.y);\n\n                // Reset the drag direction value\n                this.dragDirection = '';\n            };\n\n            // Draggabily events\n            this.draggie.on('pointerDown', this.onPointerDown);\n            this.draggie.on('dragStart', this.onDragStart);\n            this.draggie.on('dragMove', this.onDragMove);\n            this.draggie.on('pointerUp', this.onPointerUp);\n            this.draggie.on('dragEnd', this.onDragEnd);\n            \n            // Clicking the explore opens up the grid for the current menu item\n            for ( let menuItem of this.menuItems ) {\n                menuItem.DOM.explore.addEventListener('click', () => this.showContent());\n            }\n\n            // Back to menu from grid view\n            this.DOM.backToMenuCtrl.addEventListener('click', () => this.hideContent());\n\n            // Resize window: update menu position\n            window.addEventListener('resize', () => this.resize());\n        }\n        showBackCtrl() {\n            return this.toggleBackCtrl('show');\n        }\n        hideBackCtrl() {\n            return this.toggleBackCtrl('hide');\n        }\n        toggleBackCtrl(action = 'show') {\n            return new Promise((resolve, reject) => {\n                TweenMax.to(this.DOM.backToMenuCtrl, ANIMATION_SETTINGS.backCtrl.duration, {\n                    ease: ANIMATION_SETTINGS.backCtrl.ease,\n                    startAt: action === 'hide' ? null : {x: '100%'},\n                    opacity: action === 'hide' ? 0 : 1,\n                    x: action === 'hide' ? '-100%' : '0%',\n                    onComplete: resolve\n                });\n            });\n        }\n        showContent() {\n            if ( this.isAnimating ) return;\n            this.isAnimating = true;\n\n            // Cancel the render function (rAF) \n            if ( this.renderId ) {\n                window.cancelAnimationFrame(this.renderId);\n                this.renderId = undefined;\n            }\n\n            // Remove this class so we see a scrollable area now\n            this.DOM.pagePreview.classList.remove('page--preview');\n\n            let promises = [];\n            // Reset the transforms of the grid items forming again the original grid\n            promises.push(this.menuItems[this.current].imageGrid.collapse());\n            // Hide the explore link\n            promises.push(this.menuItems[this.current].hideExplore());\n            // Slide menu items letters out\n            for (let item of this.menuItems) {\n                promises.push(item.hide());\n            }\n            // Show back control\n            promises.push(this.showBackCtrl());\n            \n            Promise.all(promises).then(() => this.isAnimating = false);\n        }\n        hideContent() {\n            if ( this.isAnimating ) return;\n            this.isAnimating = true;\n\n            // First scroll to the top\n            scrollIt(0, 300, 'easeOutQuad', () => {\n                // Add this class to disable scrolling\n                this.DOM.pagePreview.classList.add('page--preview');\n                \n                // Restart the rAF loop\n                this.renderId = requestAnimationFrame(() => this.render());\n\n                let promises = [];\n                // Spread the grid items forming again the original grid\n                promises.push(this.menuItems[this.current].imageGrid.spread(true));\n                // Show the explore link\n                promises.push(this.menuItems[this.current].showExplore());\n                // Slide menu items letters in\n                for (let item of this.menuItems) {\n                    promises.push(item.show());\n                }\n                // Hide back control\n                promises.push(this.hideBackCtrl());\n                \n                Promise.all(promises).then(() => {\n                    this.isAnimating = false;\n                });\n            });\n        }\n    }\n\n    const ANIMATION_SETTINGS = {\n        // Animation settings (after the drag ends, the menu, letters and images need to be positioned or reset)\n        menu: {duration: 0.8, ease: Cubic.easeOut},\n        letters: {duration: 0.8, ease: Cubic.easeOut},\n        images: {duration: 1, ease: Quint.easeOut},\n        // Grid\n        grid: {duration: 0.8, ease: Expo.easeOut},\n        // Hiding the letters to show the images grid\n        allMenuLettersToggle: {duration: 0.8, ease: Expo.easeOut},\n        // Explore link\n        explore: {duration: 0.6, ease: Expo.easeOut},\n        // backToMenuCtrl\n        backCtrl: {duration: 0.6, ease: Expo.easeOut},\n        // Cursor arrows\n        cursor: {duration: 1, ease: Expo.easeOut},\n    };\n    \n    // Custom mouse cursor\n    const cursor = new Cursor(document.querySelector('.cursor'));\n\n    /***********************************/\n    /****** Custom cursor related ******/\n\n    // Activate the enter/leave/click methods of the custom cursor when hovering in/out on every <a> and the back to menu ctrl\n    [...document.querySelectorAll('a'), document.querySelector('button')].forEach((link) => {\n        link.addEventListener('mouseenter', () => cursor.enter());\n        link.addEventListener('mouseleave', () => cursor.leave());\n    });\n\n    /***********************************/\n    /********** Preload stuff **********/\n\n    // Preload images\n    const preloadImages = () => {\n        return new Promise((resolve, reject) => {\n            imagesLoaded(document.querySelectorAll('.grid__item'), {background: true}, resolve);\n        });\n    };\n    \n    // Preload fonts\n    const preloadFonts = () => {\n        return new Promise((resolve, reject) => {\n            WebFont.load({\n                typekit: {\n                    id: 'crf4rue'\n                },\n                active: resolve\n            });\n        });\n    };\n\n    Promise.all([\n        preloadImages(),\n        preloadFonts()  \n    ]).then(() => {\n        // the Menu\n        const menu = new Menu(document.querySelector('.menu-wrap'));\n        document.body.classList.remove('loading');\n    });\n}"
  }
]