<!DOCTYPE html>
<html>
  <head>
    <script src="https://code.createjs.com/1.0.0/easeljs.min.js"></script>
    <script src="https://code.createjs.com/1.0.0/tweenjs.min.js"></script>
    <script src="/api/assetProxy?url=http://images.neopets.com/cp/items/data/000/000/564/564507_fc3216b9b8/all-item_foreground_lower.js"></script>
  </head>
  <body>
    <div style="display: flex; justify-content: center;">
      <div
        style="
          width: 100%;
          max-width: 600px;
          border: 1px solid #aaa;
          position: relative;
        "
      >
        <div style="padding-bottom: 100%;"></div>
        <canvas
          id="stage-canvas"
          style="
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            width: 100%;
            height: 100%;
          "
        ></canvas>
      </div>
    </div>
    <div style="margin-top: 1em; text-align: center;">
      <button id="show-hide">Show/hide</button>
      <button id="pause-play">Pause/play</button>
    </div>
    <script>
      function loadImage(src) {
        return new Promise((resolve, reject) => {
          const image = new Image();
          image.onload = () => resolve(image);
          image.onerror = (e) => reject(e);
          image.src = src;
        });
      }

      function proxyUrl(url) {
        return "/api/assetProxy?url=" + encodeURIComponent(url);
      }

      async function main() {
        const composition = Object.values(AdobeAn.compositions)[0];
        const library = composition.getLibrary();

        const manifestImages = new Map(
          library.properties.manifest.map(({ id, src }) => [
            id,
            loadImage(
              proxyUrl(
                "http://images.neopets.com/cp/items/data/000/000/564/564507_fc3216b9b8/" +
                  src
              )
            ),
          ])
        );

        try {
          await Promise.all(manifestImages.values());
        } catch (e) {
          console.error("Error loading images", e);
          return;
        }

        const spriteSheets = composition.getSpriteSheet();
        for (const { name, frames } of library.ssMetadata) {
          const image = await manifestImages.get(name);
          spriteSheets[name] = new createjs.SpriteSheet({
            images: [image],
            frames,
          });
        }

        const movieClip = new library.allitem_foreground_lower();

        const canvas = document.getElementById("stage-canvas");
        const stage = new library.Stage(canvas);
        canvas.width = canvas.offsetWidth * window.devicePixelRatio;
        canvas.height = canvas.offsetHeight * window.devicePixelRatio;
        stage.scaleX =
          (canvas.offsetWidth * window.devicePixelRatio) /
          library.properties.width;
        stage.scaleY =
          (canvas.offsetHeight * window.devicePixelRatio) /
          library.properties.height;
        movieClip.cache(
          0,
          0,
          library.properties.width,
          library.properties.height
        );

        movieClip.alpha = 0;
        const tween = createjs.Tween.get(movieClip, { paused: true }).to(
          { alpha: 1 },
          200
        );
        stage.on(
          "drawend",
          () => {
            tween.paused = false;
          },
          null,
          true
        );
        stage.addChild(movieClip);

        // FPS-ish updates with RAF! https://stackoverflow.com/a/19772220/107415
        // Quite a bit going on here, the basic idea is that we want to update
        // the stage _more often_ than we update the MovieClip, because we want
        // fade-ins to happen with as high of a frame-rate as possible, but we
        // want the movie itself to run at its canonical FPS. So, we set the
        // Ticker into `requestAnimationFrame` mode to send a global tick every
        // time we get to draw, and update the stage on every such tick - but
        // we only let it tick its _children_ if enough time has passed since
        // the last one.
        const movieTickInterval = 1000 / library.properties.fps;
        let lastMovieTick = performance.now();
        let pauseMovies = false;
        createjs.Ticker.timingMode = createjs.Ticker.RAF;
        createjs.Ticker.on("tick", () => {
          const now = performance.now();
          const elapsed = now - lastMovieTick;
          if (elapsed > movieTickInterval && !pauseMovies) {
            stage.tickOnUpdate = true;
            lastMovieTick = now - (elapsed % movieTickInterval);
            movieClip.updateCache();
          } else {
            stage.tickOnUpdate = false;
          }
          stage.update();
        });

        document.getElementById("show-hide").addEventListener("click", () => {
          tween.reversed = !tween.reversed;
          tween.setPosition(0);
          tween.paused = false;
        });

        document.getElementById("pause-play").addEventListener("click", () => {
          pauseMovies = !pauseMovies;
        });
      }

      main();
    </script>
  </body>
</html>