master f9f535c65af0 cached
204 files
594.8 KB
160.8k tokens
114 symbols
1 requests
Download .txt
Showing preview only (647K chars total). Download the full file or copy to clipboard to get everything.
Repository: pothonprogramming/pothonprogramming.github.io
Branch: master
Commit: f9f535c65af0
Files: 204
Total size: 594.8 KB

Directory structure:
gitextract_49aaihe4/

├── content/
│   ├── 2018/
│   │   └── minkowski-difference/
│   │       └── minkowski-difference.html
│   ├── animation/
│   │   ├── animation.css
│   │   ├── animation.html
│   │   └── animation.js
│   ├── animation-game-loop/
│   │   ├── animation.css
│   │   ├── animation.html
│   │   └── animation.js
│   ├── better-tile/
│   │   ├── better-tile.html
│   │   └── better-tile.js
│   ├── better-tile-graphics/
│   │   ├── better-tile-graphics.html
│   │   └── better-tile-graphics.js
│   ├── blit/
│   │   ├── blit.css
│   │   ├── blit.html
│   │   └── blit.js
│   ├── bouncing-polygons/
│   │   └── bouncing-polygons.html
│   ├── calculator/
│   │   ├── calculator.css
│   │   ├── calculator.html
│   │   └── calculator.js
│   ├── canvas/
│   │   ├── canvas.css
│   │   ├── canvas.html
│   │   └── canvas.js
│   ├── circle-collision-detection/
│   │   └── circle-collision-detection.html
│   ├── circle-collision-response/
│   │   └── circle-collision-response.html
│   ├── collision/
│   │   ├── collision.css
│   │   ├── collision.html
│   │   └── collision.js
│   ├── control/
│   │   ├── control.css
│   │   ├── control.html
│   │   └── control.js
│   ├── cube/
│   │   └── cube.html
│   ├── dino/
│   │   ├── dino.css
│   │   ├── dino.html
│   │   └── dino.js
│   ├── dominiques-doors/
│   │   ├── area0.json
│   │   ├── area1.json
│   │   ├── area2.json
│   │   ├── area3.json
│   │   ├── dominiques-doors.css
│   │   ├── dominiques-doors.html
│   │   └── dominiques-doors.js
│   ├── elements/
│   │   ├── elements.html
│   │   └── elements.js
│   ├── gjk/
│   │   └── gjk.html
│   ├── hello-world/
│   │   └── hello.html
│   ├── hit-the-wall/
│   │   ├── hit-the-wall.css
│   │   ├── hit-the-wall.html
│   │   └── hit-the-wall.js
│   ├── hitbox/
│   │   └── hitbox.html
│   ├── https-server/
│   │   ├── index.css
│   │   ├── index.html
│   │   ├── server.js
│   │   └── ssl/
│   │       ├── crt.cnf
│   │       ├── crt.pfx
│   │       ├── csr.cnf
│   │       ├── make-crt.sh
│   │       ├── make-csr.sh
│   │       ├── make-key.sh
│   │       └── make-pfx.sh
│   ├── indexed-db/
│   │   ├── index.css
│   │   └── index.html
│   ├── inheritance/
│   │   ├── inheritance.html
│   │   └── inheritance.js
│   ├── inventory/
│   │   └── inventory.html
│   ├── ipo/
│   │   ├── components/
│   │   │   ├── input.js
│   │   │   ├── main.js
│   │   │   ├── output.js
│   │   │   └── processor.js
│   │   └── ipo.html
│   ├── json/
│   │   ├── json.html
│   │   ├── json.js
│   │   └── json.json
│   ├── load-image/
│   │   ├── load-image.css
│   │   ├── load-image.html
│   │   └── load-image.js
│   ├── multiple-inheritance/
│   │   ├── multiple-inheritance.html
│   │   └── multiple-inheritance.js
│   ├── objects-and-vars/
│   │   ├── objects.html
│   │   └── objects.js
│   ├── offline-web-app/
│   │   ├── manifest.json
│   │   ├── server.js
│   │   ├── ssl/
│   │   │   └── crt.pfx
│   │   ├── web-app-service.js
│   │   ├── web-app.css
│   │   └── web-app.html
│   ├── pagination/
│   │   ├── article1.txt
│   │   ├── article2.txt
│   │   ├── article3.txt
│   │   ├── article4.txt
│   │   ├── article5.txt
│   │   ├── pagination.css
│   │   ├── pagination.html
│   │   └── paginator.js
│   ├── particle-pool/
│   │   └── particle-pool.html
│   ├── platform/
│   │   └── platform.html
│   ├── platformer-ai/
│   │   └── platformer-ai.html
│   ├── polygon/
│   │   └── polygon.html
│   ├── polygon-rotation/
│   │   └── polygon-rotation.html
│   ├── pre-scale-performance/
│   │   ├── pre-scale-performance.css
│   │   ├── pre-scale-performance.html
│   │   └── pre-scale-performance.js
│   ├── prototype-inheritance/
│   │   ├── prototype-inheritance.html
│   │   └── prototype-inheritance.js
│   ├── rabbit-trap/
│   │   ├── 01/
│   │   │   ├── controller-01.js
│   │   │   ├── display-01.js
│   │   │   ├── engine-01.js
│   │   │   ├── game-01.js
│   │   │   └── main-01.js
│   │   ├── 02/
│   │   │   ├── controller-02.js
│   │   │   ├── display-02.js
│   │   │   ├── game-02.js
│   │   │   └── main-02.js
│   │   ├── 03/
│   │   │   ├── display-03.js
│   │   │   ├── game-03.js
│   │   │   └── main-03.js
│   │   ├── 04/
│   │   │   ├── display-04.js
│   │   │   └── game-04.js
│   │   ├── 05/
│   │   │   ├── display-05.js
│   │   │   ├── game-05.js
│   │   │   └── main-05.js
│   │   ├── 06/
│   │   │   ├── engine-06.js
│   │   │   ├── game-06.js
│   │   │   ├── main-06.js
│   │   │   ├── script.txt
│   │   │   ├── zone00.json
│   │   │   ├── zone01.json
│   │   │   ├── zone02.json
│   │   │   ├── zone03.json
│   │   │   └── zone04.json
│   │   ├── 07/
│   │   │   ├── game-07.js
│   │   │   ├── main-07.js
│   │   │   └── zone00.json
│   │   ├── rabbit-trap.css
│   │   └── rabbit-trap.html
│   ├── rectangle-collision/
│   │   └── rectangle-collision.html
│   ├── shoot/
│   │   └── shoot.html
│   ├── snake/
│   │   ├── snake.css
│   │   ├── snake.html
│   │   └── snake.js
│   ├── square-collision-response/
│   │   ├── response.css
│   │   ├── response.html
│   │   └── response.js
│   ├── starfield/
│   │   └── starfield.html
│   ├── stay-down/
│   │   ├── game-states/
│   │   │   ├── pause.js
│   │   │   ├── run.js
│   │   │   └── title.js
│   │   ├── initialize.js
│   │   ├── stay-down.html
│   │   ├── stay-down.js
│   │   ├── tools/
│   │   │   ├── controller.js
│   │   │   ├── engine.js
│   │   │   ├── loader.js
│   │   │   ├── state-manager.js
│   │   │   └── text.js
│   │   └── utilities/
│   │       ├── buffer.js
│   │       ├── collider.js
│   │       ├── frame.js
│   │       ├── item.js
│   │       ├── platform.js
│   │       ├── player.js
│   │       └── rectangle-2d.js
│   ├── tile-animation/
│   │   └── tile-animation.html
│   ├── tile-graphics/
│   │   ├── tile-graphics.css
│   │   ├── tile-graphics.html
│   │   └── tile-graphics.js
│   ├── tile-grid/
│   │   ├── tile-grid.css
│   │   ├── tile-grid.html
│   │   └── tile-grid.js
│   ├── tile-scroll/
│   │   └── tile-scroll.html
│   ├── tile-types/
│   │   ├── tile-types.css
│   │   ├── tile-types.html
│   │   └── tile-types.js
│   ├── tile-world/
│   │   ├── tile-world.css
│   │   ├── tile-world.html
│   │   └── tile-world.js
│   ├── top-down-tiles/
│   │   ├── top-down-tiles.css
│   │   ├── top-down-tiles.html
│   │   └── top-down-tiles.js
│   ├── touch-controller/
│   │   ├── touch-controller.css
│   │   ├── touch-controller.html
│   │   └── touch-controller.js
│   ├── vector-math/
│   │   └── vector-math.html
│   ├── walk-on-tiles/
│   │   ├── walk-on-tiles.css
│   │   ├── walk-on-tiles.html
│   │   └── walk-on-tiles.js
│   ├── web-app/
│   │   ├── manifest.json
│   │   ├── server.js
│   │   ├── ssl/
│   │   │   └── crt.pfx
│   │   ├── web-app.css
│   │   └── web-app.html
│   ├── wmw-basic/
│   │   └── basic.html
│   └── wmw-bouncing-balls/
│       └── bouncing-balls.html
├── data/
│   ├── logs.json
│   └── projects.json
├── index.css
├── index.html
├── index.js
├── library/
│   └── dom-kit.js
├── log.css
├── project.css
├── robots.txt
├── server.js
├── theme.css
└── tools/
    ├── log.js
    └── project.js

================================================
FILE CONTENTS
================================================

================================================
FILE: content/2018/minkowski-difference/minkowski-difference.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "description" content = "Get The Minkowski Difference between Arbitrary Convex Shapes.">
    <meta name = "viewport" content = "width=device-width, user-scalable=no">

    <title>Minkowski Difference</title>

    <style>

      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }

      body, html { height:100%; width:100%; }

      body { background-color:#202830; display:grid; }

      div {

        display:grid;
        grid-template-columns:auto auto auto;
        left:16px;
        position:fixed;
        right:16px;
        top:16px;

      }

      a {

        align-self:center;
        background-color:#202830;
        border-radius:16px;
        color:#ffffff;
        cursor:pointer;
        font-size:1.5em;
        font-weight:700;
        justify-self:center;
        padding:8px 32px;
        pointer-events:auto;
        text-align:center;


      }

      canvas { align-self:center; display:grid; justify-self:center; }
    
    </style>

  </head>

  <body>

    <div>
      <a>circle</a>
      <a>polygon</a>
      <a>rectangle</a>
    </div>

    <canvas></canvas>

    <script type = "text/javascript">

        /////////////////
       //// CLASSES ////
      /////////////////

      class Point2D {

        constructor(x, y) {

          this.x = x; this.y = y;

        }

        setPosition(x, y) {

          this.x = x; this.y = y;

        }

        setPositionPlus(x, y) {

          this.x += x;
          this.y += y;

        }

      }
      
      class Circle2D {

        constructor(x, y, radius) {

          this.position = new Point2D(x, y);
          this.radius = radius;
          this.color = "rgba(0, 0, 0, 0.5)";

        }

        static createMinkowskiDifferenceShape(circle_a, circle_b) {

          return new Circle2D(circle_a.position.x - circle_b.position.x, circle_a.position.y - circle_b.position.y, circle_a.radius + circle_b.radius);

        }

        draw(context, offset) {

          context.beginPath();
          context.arc(this.position.x + offset.x, this.position.y + offset.y, this.radius, 0, Math.PI * 2, false);
          context.fillStyle = this.color;
          context.fill();

        }

        setPosition(x, y) {

          this.position.setPosition(x, y);

        }

      }

      class Polygon2D {

        constructor(x, y, ...vertices) {
          
          this.position = new Point2D(x, y);
          this.vertices = new Array();
          this.color = "rgba(0, 0, 0, 0.5)";

          for (let index = vertices.length - 2; index > -1; index -= 2) {

            this.vertices[index * 0.5] = new Point2D(vertices[index] + x, vertices[index + 1] + y);

          }

        }

        static createMinkowskiDifferenceShape(polygon_a, polygon_b) {

          var vertices = new Array();
          
          for (var vertex_a of polygon_a.vertices) {

            for (var vertex_b of polygon_b.vertices) {

              vertices.push(vertex_a.x - vertex_b.x, vertex_a.y - vertex_b.y);

            }

          }

          return new Polygon2D(0, 0, ...vertices);

        }

        draw(context, offset) {

          var vertex = this.vertices[0];

          context.beginPath();
          context.fillStyle = this.color;
          context.moveTo(vertex.x + offset.x, vertex.y + offset.y);

          for (var vertex of this.vertices) {
            
            context.lineTo(vertex.x + offset.x, vertex.y + offset.y);
            
          }
          
          context.closePath();
          context.fill();

        }

        setPosition(x, y) {

          var vector_x = x - this.position.x;
          var vector_y = y - this.position.y;

          for (var vertex of this.vertices) {

            vertex.setPositionPlus(vector_x, vector_y);

          }

          this.position.setPositionPlus(vector_x, vector_y);

        }

      }

      class Rectangle2D {

        constructor(x, y, width, height) {

          this.position = new Point2D(x, y);
          this.width = width; this.height = height;
          this.color = "rgba(0, 0, 0, 0.5)";

        }

        static createMinkowskiDifferenceShape(rectangle_a, rectangle_b) {

          var x = rectangle_a.position.x - rectangle_b.position.x - rectangle_b.width;
          var y = rectangle_a.position.y - rectangle_b.position.y - rectangle_b.height;

          return new Rectangle2D(x, y, rectangle_a.width + rectangle_b.width, rectangle_a.height + rectangle_b.height);

        }

        draw(context, offset) {

          context.beginPath();
          context.rect(this.position.x + offset.x, this.position.y + offset.y, this.width, this.height);
          context.fillStyle = this.color;
          context.fill();

        }

        setPosition(x, y) {

          this.position.setPosition(x, y);

        }

      }

        ///////////////////////////
       //// APPLICATION LOGIC ////
      ///////////////////////////

      var context = document.querySelector("canvas").getContext("2d");
      var offset = new Point2D(undefined, undefined); // Offset of the on screen origin
      var pointer = {

        x:0, y:0

      };

      var circles = [

        new Circle2D(100, 100, 100),
        new Circle2D(200, 200, 100)

      ];

      var rectangles = [

        new Rectangle2D(100, 120, 100, 70),
        new Rectangle2D(150, 175, 80, 50)

      ];

      var polygons = [
        
        new Polygon2D(100, 100, -100, 50, 0, -100, 100, 0, 0, 100),
        new Polygon2D(100, 100, -100, 0, 0, -100, 100, 0, 0, 100)

      ];

      var shapes = circles;

      function cycle(time_stamp) {

        window.requestAnimationFrame(cycle);

        update();
        render();

      }

      function render() {

        // Fill the entire background
        context.fillStyle = "#ffffff";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        // Draw the x and y axes
        context.beginPath();
        context.strokeStyle = "#202830";
        context.moveTo(offset.x, 0);
        context.lineTo(offset.x, context.canvas.height);
        context.stroke();
        context.moveTo(0, offset.y);
        context.lineTo(context.canvas.width, offset.y);
        context.stroke();

        // Draw the shapes
        for (var shape of shapes) {

          shape.draw(context, offset);

        }

        shape.setPosition(pointer.x, pointer.y);

        var minkowski_difference_shape = shape.constructor.createMinkowskiDifferenceShape(shape, shapes[0]);

        minkowski_difference_shape.draw(context, offset);
   
      }

      function update() {


      }

        ////////////////////////
       //// EVENT HANDLERS ////
      ////////////////////////

      function clickA(event) { event.preventDefault();

        switch(this.innerText) {

          case "circle":    shapes = circles;    break;
          case "polygon":   shapes = polygons;   break;
          case "rectangle": shapes = rectangles;

        }

        shapes[0].setPosition(Math.floor(Math.random() * context.canvas.width * 0.5), Math.floor(Math.random() * context.canvas.height * 0.5));

      }

      // Get the position of the pointer and don't forget to add the offset.
      function mouseMove(event) { event.preventDefault();

        var rectangle = context.canvas.getBoundingClientRect();

        pointer.x = event.clientX - rectangle.left - offset.x;
        pointer.y = event.clientY - rectangle.top  - offset.y;

      }

      function resize(event) {

        context.canvas.width  = document.documentElement.clientWidth - 16;
        context.canvas.height = document.documentElement.clientHeight - 16;

        context.canvas.imageSmoothingEnabled = false;

        offset.x = Math.round(context.canvas.width * 0.5);
        offset.y = Math.round(context.canvas.height * 0.5);

      }

        ////////////////////
       //// INITIALIZE ////
      ////////////////////

      document.querySelectorAll("a").forEach((a) => {

        a.addEventListener("click", clickA);

      }); // Add the click event listener to each a element.

      window.addEventListener("resize", resize);

      window.addEventListener("mousemove", mouseMove);

      resize(); // Make the canvas fit the screen

      cycle(); // Start the application loop

    </script>

  </body>

</html>

================================================
FILE: content/animation/animation.css
================================================
/* Frank Poth 12/23/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

html {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  grid-template-columns:auto;
  grid-template-rows:auto auto auto;
  height:100%;
  justify-items:center;
  padding:0 8px;
  width:100%;

}


================================================
FILE: content/animation/animation.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <meta name = "description" content = "A working example of sprite animation.">

    <link href = "animation.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Sprite Animation</h1>

    <canvas></canvas>

    <p>Use the keyboard to walk left and right. Also, press up to jump.</p>

    <script src = "animation.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/animation/animation.js
================================================
// Frank Poth 12/23/2017

/* This example will show you how to do custom sprite animation in JavaScript.
It uses an Animation class that handles updating and changing a sprite's current
frame, and a sprite_sheet object to hold the source image and the different animation
frame sets. */

(function() { "use strict";

  /* Each sprite sheet tile is 16x16 pixels in dimension. */
  const SPRITE_SIZE = 16;

  /* The Animation class manages frames within an animation frame set. The frame
  set is an array of values that correspond to the location of sprite images in
  the sprite sheet. For example, a frame value of 0 would correspond to the first
  sprite image / tile in the sprite sheet. By arranging these values in a frame set
  array, you can create a sequence of frames that make an animation when played in
  quick succession. */
  var Animation = function(frame_set, delay) {

    this.count = 0;// Counts the number of game cycles since the last frame change.
    this.delay = delay;// The number of game cycles to wait until the next frame change.
    this.frame = 0;// The value in the sprite sheet of the sprite image / tile to display.
    this.frame_index = 0;// The frame's index in the current animation frame set.
    this.frame_set = frame_set;// The current animation frame set that holds sprite tile values.

  };

  Animation.prototype = {

    /* This changes the current animation frame set. For example, if the current
    set is [0, 1], and the new set is [2, 3], it changes the set to [2, 3]. It also
    sets the delay. */
    change:function(frame_set, delay = 15) {

      if (this.frame_set != frame_set) {// If the frame set is different:

        this.count = 0;// Reset the count.
        this.delay = delay;// Set the delay.
        this.frame_index = 0;// Start at the first frame in the new frame set.
        this.frame_set = frame_set;// Set the new frame set.
        this.frame = this.frame_set[this.frame_index];// Set the new frame value.

      }

    },

    /* Call this on each game cycle. */
    update:function() {

      this.count ++;// Keep track of how many cycles have passed since the last frame change.

      if (this.count >= this.delay) {// If enough cycles have passed, we change the frame.

        this.count = 0;// Reset the count.
        /* If the frame index is on the last value in the frame set, reset to 0.
        If the frame index is not on the last value, just add 1 to it. */
        this.frame_index = (this.frame_index == this.frame_set.length - 1) ? 0 : this.frame_index + 1;
        this.frame = this.frame_set[this.frame_index];// Change the current frame value.

      }

    }

  };

  var buffer, controller, display, loop, player, render, resize, sprite_sheet;

  buffer = document.createElement("canvas").getContext("2d");
  display = document.querySelector("canvas").getContext("2d");

  /* I made some changes to the controller object. */
  controller = {

    /* Now each key object knows its physical state as well as its active state.
    When a key is active it is used in the game logic, but its physical state is
    always recorded and never altered for reference. */
    left:  { active:false, state:false },
    right: { active:false, state:false },
    up:    { active:false, state:false },

    keyUpDown:function(event) {

      /* Get the physical state of the key being pressed. true = down false = up*/
      var key_state = (event.type == "keydown") ? true : false;

      switch(event.keyCode) {

        case 37:// left key

          /* If the virtual state of the key is not equal to the physical state
          of the key, we know something has changed, and we must update the active
          state of the key. By doing this it prevents repeat firing of keydown events
          from altering the active state of the key. Basically, when you are jumping,
          holding the jump key down isn't going to work. You'll have to hit it every
          time, but only if you set the active key state to false when you jump. */
          if (controller.left.state != key_state) controller.left.active = key_state;
          controller.left.state  = key_state;// Always update the physical state.

        break;
        case 38:// up key

          if (controller.up.state != key_state) controller.up.active = key_state;
          controller.up.state  = key_state;

        break;
        case 39:// right key

          if (controller.right.state != key_state) controller.right.active = key_state;
          controller.right.state  = key_state;

        break;

      }

      //console.log("left:  " + controller.left.state + ", " + controller.left.active + "\nright: " + controller.right.state + ", " + controller.right.active + "\nup:    " + controller.up.state + ", " + controller.up.active);

    }

  };

  /* The player object is just a rectangle with an animation object. */
  player = {

    animation:new Animation(),// You don't need to setup Animation right away.
    jumping:true,
    height:16,    width:16,
    x:0,          y:40 - 18,
    x_velocity:0, y_velocity:0

  };

  /* The sprite sheet object holds the sprite sheet graphic and some animation frame
  sets. An animation frame set is just an array of frame values that correspond to
  each sprite image in the sprite sheet, just like a tile sheet and a tile map. */
  sprite_sheet = {

    frame_sets:[[0, 1], [2, 3], [4, 5]],// standing still, walk right, walk left
    image:new Image()

  };

  loop = function(time_stamp) {

    if (controller.up.active && !player.jumping) {

      controller.up.active = false;
      player.jumping = true;
      player.y_velocity -= 2.5;

    }

    if (controller.left.active) {

      /* To change the animation, all you have to do is call animation.change. */
      player.animation.change(sprite_sheet.frame_sets[2], 15);
      player.x_velocity -= 0.05;

    }

    if (controller.right.active) {

      player.animation.change(sprite_sheet.frame_sets[1], 15);
      player.x_velocity += 0.05;

    }

    /* If you're just standing still, change the animation to standing still. */
    if (!controller.left.active && !controller.right.active) {

      player.animation.change(sprite_sheet.frame_sets[0], 20);

    }

    player.y_velocity += 0.25;

    player.x += player.x_velocity;
    player.y += player.y_velocity;
    player.x_velocity *= 0.9;
    player.y_velocity *= 0.9;

    if (player.y + player.height > buffer.canvas.height - 2) {

      player.jumping = false;
      player.y = buffer.canvas.height - 2 - player.height;
      player.y_velocity = 0;

    }

    if (player.x + player.width < 0) {

      player.x = buffer.canvas.width;

    } else if (player.x > buffer.canvas.width) {

      player.x = - player.width;

    }

    player.animation.update();

    render();

    window.requestAnimationFrame(loop);

  };

  render = function() {

    /* Draw the background. */
    buffer.fillStyle = "#7ec0ff";
    buffer.fillRect(0, 0, buffer.canvas.width, buffer.canvas.height);
    buffer.strokeStyle = "#8ed0ff";
    buffer.lineWidth = 10;
    buffer.beginPath();
    buffer.moveTo(0, 0);
    buffer.bezierCurveTo(40, 20, 40, 0, 80, 0);
    buffer.moveTo(0, 0);
    buffer.bezierCurveTo(40, 20, 40, 20, 80, 0);
    buffer.stroke();
    buffer.fillStyle = "#009900";
    buffer.fillRect(0, 36, buffer.canvas.width, 4);

    /* When you draw your sprite, just use the animation frame value to determine
    where to cut your image from the sprite sheet. It's the same technique used
    for cutting tiles out of a tile sheet. Here I have a very easy implementation
    set up because my sprite sheet is only a single row. */

    /* 02/07/2018 I added Math.floor to the player's x and y positions to eliminate
    antialiasing issues. Take out the Math.floor to see what I mean. */
    buffer.drawImage(sprite_sheet.image, player.animation.frame * SPRITE_SIZE, 0, SPRITE_SIZE, SPRITE_SIZE, Math.floor(player.x), Math.floor(player.y), SPRITE_SIZE, SPRITE_SIZE);

    display.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, display.canvas.width, display.canvas.height);

  };

  resize = function() {

    display.canvas.width = document.documentElement.clientWidth - 32;

    if (display.canvas.width > document.documentElement.clientHeight) {

      display.canvas.width = document.documentElement.clientHeight;

    }

    display.canvas.height = display.canvas.width * 0.5;

    display.imageSmoothingEnabled = false;

  };

      ////////////////////
    //// INITIALIZE ////
  ////////////////////

  buffer.canvas.width = 80;
  buffer.canvas.height = 40;

  window.addEventListener("resize", resize);

  window.addEventListener("keydown", controller.keyUpDown);
  window.addEventListener("keyup", controller.keyUpDown);

  resize();

  sprite_sheet.image.addEventListener("load", function(event) {// When the load event fires, do this:

    window.requestAnimationFrame(loop);// Start the game loop.

  });

  sprite_sheet.image.src = "animation.png";// Start loading the image.

})();


================================================
FILE: content/animation-game-loop/animation.css
================================================
/* Frank Poth 08/12/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

html, body {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  justify-content:center;

}

canvas {

  background-color:#ffffff;

}


================================================
FILE: content/animation-game-loop/animation.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <title>Animation</title>
    <link href = "animation.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Animation</h1>
    <canvas></canvas>

    <script src = "animation.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/animation-game-loop/animation.js
================================================
// Frank Poth 08/12/2017

var context, rectangle, loop;

context = document.querySelector("canvas").getContext("2d");

context.canvas.height = 180;
context.canvas.width = 320;

rectangle = {

  height:32,
  width:32,
  x:0,
  y:72, // Center of the canvas

};

loop = function() {

  rectangle.x += 1;

  context.fillStyle = "#202020";
  context.fillRect(0, 0, 320, 180);// x, y, width, height
  context.fillStyle = "#ff0000";// hex for red
  context.beginPath();
  context.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  context.fill();

  if (rectangle.x > 320) {// if rectangle goes past right boundary

    rectangle.x = -32;

  }

  // call update when the browser is ready to draw again
  window.requestAnimationFrame(loop);

};

window.requestAnimationFrame(loop);


================================================
FILE: content/better-tile/better-tile.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta charset = "utf-8">
    <meta name = "author"       content = "Frank Poth">
    <meta name = "description"  content = "">
    <meta name = "theme-color"  content = "#202830">
    <meta name = "viewport"     content = "height=device-height,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no,width=device-width">

    <style>

      * {
      
        box-sizing:border-box;
        margin:0;
        overflow:hidden;
        padding:0;
        user-select:none;
      
      }

      html, body { height:100%; width:100%; }

      body {
      
        align-content:center;
        background-color:#202830;
        display:grid;
        justify-content:center;
      
      }

      canvas { image-rendering:pixelated; image-rendering:-moz-crisp-edges; }

    </style>

    <title>Better Tile</title>

  </head>

  <body>

    <canvas></canvas>

    <script src = "better-tile.js"></script>

  </body>

</html>

================================================
FILE: content/better-tile/better-tile.js
================================================
(() => {

  // The display canvas' context. Draw the tile buffer here.
  const DISPLAY = document.querySelector('canvas').getContext('2d', { alpha:false, desynchronized:false });
  // The tile buffer canvas' context. Draw individual tiles here.
  const BUFFER  = document.createElement('canvas').getContext('2d', { alpha:false, desynchronized:true });

  // This is the width and height for every tile.
  const TILE_SIZE = 16;

  // The TILES object contains "tile" objects with keys that correspond to the map values.
  // Each tile object has a color.
  const TILES = {

    0: { color:'#d8f4f4' }, // sky
    1: { color:'#ffffff' }, // cloud
    2: { color:'#3e611e' }, // grass
    3: { color:'#412823' }  // dirt

  }

  // The map holds all the info about the map we will be drawing, including the tile indices array.
  const MAP = {

    columns: 16,
    rows:    14,
    height:  14 * TILE_SIZE,
    width:   16 * TILE_SIZE,

    // This is used during image scaling to ensure the rendered image is not skewed.
    width_height_ratio: 16 / 14,

    // The values in this array correspond to the keys in the TILES object.
    tiles:[1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,
           0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,
           0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,
           0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,
           0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
           0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,
           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
           0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,
           2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
           2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,
           3,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,
           3,3,3,3,3,3,3,0,0,0,3,3,3,3,3,3]

  };

  // This will render tiles to the buffer.
  function renderTiles() {

    var map_index = 0; // This represents the position in the MAP.tiles array we're getting our tile value from.

    // Increment by the actual TILE_SIZE to avoid having to multiply on every iteration.
    for (var top = 0; top < MAP.height; top += TILE_SIZE) {

      for (var left = 0; left < MAP.width; left += TILE_SIZE) {

        var tile_value = MAP.tiles[map_index]; // Get the tile value from the map.
        
        var tile = TILES[tile_value]; // Get the specific tile object from the TILES object.

        BUFFER.fillStyle = tile.color; // Now that we have the tile we can access its properties.

        BUFFER.fillRect(left, top, TILE_SIZE, TILE_SIZE); // Draw the tile at the left, top position and TILE_SIZE.

        map_index ++; // Make sure to increment the map_index so we can get the next tile from the map.

      }

    }

  }

  // Render the buffer to the display.
  // If this example required a game loop or repeated draws, this would be your main rendering function.
  // The benefit of this approach is that you only make 1 drawImage call here instead of 1 call for every tile.
  function renderDisplay() {

    DISPLAY.drawImage(BUFFER.canvas, 0, 0);

  }

  // This function resizes the CSS width and height of the DISPLAY canvas to force it to scale to fit the window.
  function resize(event) {

    // Get the height and width of the window
    var height = document.documentElement.clientHeight;
    var width  = document.documentElement.clientWidth;

    // This makes sure the DISPLAY canvas is resized in a way that maintains the MAP's width / height ratio.
    if (width / height < MAP.width_height_ratio) height = Math.floor(width  / MAP.width_height_ratio);
    else                                         width  = Math.floor(height * MAP.width_height_ratio);

    // This sets the CSS of the DISPLAY canvas to resize it to the scaled height and width.
    DISPLAY.canvas.style.height = height + 'px';
    DISPLAY.canvas.style.width  = width  + 'px';

  }

  // Set the initial width and height of the BUFFER and the DISPLAY canvases.
  BUFFER.canvas.width  = DISPLAY.canvas.width  = MAP.width;
  BUFFER.canvas.height = DISPLAY.canvas.height = MAP.height;

  // To ensure there is no anti-aliasing when drawing to the canvas, set image smoothing to false on both canvases.
  BUFFER.imageSmoothingEnabled = DISPLAY.imageSmoothingEnabled = false;

  // Draw the individual tiles to the buffer.
  renderTiles();

  // Draw the BUFFER to the DISPLAY canvas.
  renderDisplay();

  // Add the resize event listener.
  window.addEventListener('resize', resize);

  // Calling resize forces the DISPLAY canvas to be scaled by the CSS.
  resize();

})();

================================================
FILE: content/better-tile-graphics/better-tile-graphics.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta charset = "utf-8">
    <meta name = "author"       content = "Frank Poth">
    <meta name = "description"  content = "Use a canvas buffer to store a full tile map image to reduce draw calls and save memory.">
    <meta name = "theme-color"  content = "#202830">
    <meta name = "viewport"     content = "height=device-height,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no,width=device-width">

    <style>

      * {
      
        box-sizing:border-box;
        margin:0;
        overflow:hidden;
        padding:0;
        user-select:none;
      
      }

      html, body { height:100%; width:100%; }

      body {
      
        align-content:center;
        background-color:#202830;
        display:grid;
        justify-content:center;
      
      }

      canvas { image-rendering:pixelated; image-rendering:-moz-crisp-edges; }

    </style>

    <title>Better Tile Graphics</title>

  </head>

  <body>

    <canvas></canvas>

    <script src = "better-tile-graphics.js"></script>

  </body>

</html>

================================================
FILE: content/better-tile-graphics/better-tile-graphics.js
================================================
(() => {

  // The display canvas' context. Draw the tile buffer here. It's important not to desynchronize when using CSS to scale.
  const DISPLAY = document.querySelector('canvas').getContext('2d', { alpha:false, desynchronized:false });
  // The tile buffer canvas' context. Draw individual tiles here.
  const BUFFER  = document.createElement('canvas').getContext('2d', { alpha:false, desynchronized:true });

  // This is the width and height for every tile.
  const TILE_SIZE = 16;

  // This image will hold the tile sheet once it is loaded.
  const TILE_SHEET_IMAGE = new Image();

  // The map holds all the info about the map we will be drawing, including the tile indices array.
  const MAP = {

    columns: 16,
    rows:    14,
    height:  14 * TILE_SIZE,
    width:   16 * TILE_SIZE,

    // This is used during image scaling to ensure the rendered image is not skewed.
    width_height_ratio: 16 / 14,

    // The values in this array correspond to the keys in the TILES object.
    tiles:[10,22,22,22,22,22,22,22,22,22,22,22,23,-1, 8,15,
           20, 1, 2, 3, 2, 1, 1, 3, 3, 2, 1, 1, 3,-1, 9,19,
           20,-1, 7, 5, 0, 6, 6,-1,-1,-1,-1,-1, 4, 5, 6,19,
           20,-1, 8,15,16,17,18,-1,-1,-1, 6, 5,15,16,17,10,
           20,-1, 8,19,11,12,23,-1,-1,-1,15,17,10,11,10,10,
           20,-1, 8,19,10,23, 3,-1,-1,-1,21,22,22,22,22,13,
           20,-1, 8,19,20, 2,-1,-1,-1,-1, 3, 2, 1, 1, 2,19,
           20,-1, 8,19,20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,19,
           20,-1, 8,19,20, 5, 5, 6, 4, 6, 5, 4, 4, 0, 6,19,
           20,-1, 8,19,10,17,16,17,17,16,17,17,17,17,16,10,
           20,-1, 8,21,22,22,22,22,22,22,22,22,22,22,22,10,
           20,-1, 9, 3, 1, 1, 2, 3, 3, 2, 1, 2, 3, 1, 1,19,
           20, 4, 5, 6, 6, 5, 4, 4, 5, 4, 6, 5, 6,-1, 7,19,
           10,16,17,17,16,17,17,17,16,17,17,16,18,-1, 8,19]

  };

  // This will calculate the tile's source position in the tile sheet given the number of columns in the tile sheet and the index of the tile in the tile sheet.
  function calculateTileSourcePosition(tile_index, tile_sheet_columns) {

    return {
      
      x:           tile_index % tile_sheet_columns  * TILE_SIZE,
      y:Math.floor(tile_index / tile_sheet_columns) * TILE_SIZE
    
    };

  }

  // This will render tiles to the buffer.
  function renderTiles() {

    var map_index = 0; // This represents the position in the MAP.tiles array we're getting our tile value from.

    // Increment by the actual TILE_SIZE to avoid having to multiply on every iteration.
    for (var top = 0; top < MAP.height; top += TILE_SIZE) {

      for (var left = 0; left < MAP.width; left += TILE_SIZE) {

        var tile_value = MAP.tiles[map_index]; // Get the tile value from the map.

        map_index ++; // Make sure to increment the map_index so we can get the next tile from the map.

        if (tile_value == -1) continue; // If the tile space is meant to be empty, skip this iteration.
        
        var tile_source_position = calculateTileSourcePosition(tile_value, 6); // Get the specific tile object from the TILES object.

        BUFFER.drawImage(TILE_SHEET_IMAGE, tile_source_position.x, tile_source_position.y, TILE_SIZE, TILE_SIZE, left, top, TILE_SIZE, TILE_SIZE);

      }

    }

  }

  // Render the buffer to the display.
  // If this example required a game loop or repeated draws, this would be your main rendering function.
  // The benefit of this approach is that you only make 1 drawImage call here instead of 1 call for every tile.
  function renderDisplay() {

    DISPLAY.drawImage(BUFFER.canvas, 0, 0);

  }

  // This function resizes the CSS width and height of the DISPLAY canvas to force it to scale to fit the window.
  function resize(event) {

    // Get the height and width of the window
    var height = document.documentElement.clientHeight;
    var width  = document.documentElement.clientWidth;

    // This makes sure the DISPLAY canvas is resized in a way that maintains the MAP's width / height ratio.
    if (width / height < MAP.width_height_ratio) height = Math.floor(width  / MAP.width_height_ratio);
    else                                         width  = Math.floor(height * MAP.width_height_ratio);

    // This sets the CSS of the DISPLAY canvas to resize it to the scaled height and width.
    DISPLAY.canvas.style.height = height + 'px';
    DISPLAY.canvas.style.width  = width  + 'px';

  }

  // Set the initial width and height of the BUFFER and the DISPLAY canvases.
  BUFFER.canvas.width  = DISPLAY.canvas.width  = MAP.width;
  BUFFER.canvas.height = DISPLAY.canvas.height = MAP.height;

  // To ensure there is no anti-aliasing when drawing to the canvas, set image smoothing to false on both canvases.
  BUFFER.imageSmoothingEnabled = DISPLAY.imageSmoothingEnabled = false;

  // Add the resize event listener.
  window.addEventListener('resize', resize);

  TILE_SHEET_IMAGE.addEventListener('load', function(event) {

    // Draw the individual tiles to the buffer.
    renderTiles();

    // Draw the BUFFER to the DISPLAY canvas.
    renderDisplay();

    // Calling resize forces the DISPLAY canvas to be scaled by the CSS.
    resize();

  }, { once:true });
  
  TILE_SHEET_IMAGE.src = 'better-tile-graphics.png';

})();

================================================
FILE: content/blit/blit.css
================================================
/* Frank Poth 12/26/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;
  user-select:none;

}

html {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  grid-template-columns:auto;
  grid-template-rows:auto auto auto auto;
  height:100%;
  justify-items:center;
  padding:0 8px;
  width:100%;

}

div {

  align-content:space-around;
  display:grid;
  grid-column-gap:8px;
  grid-row-gap:8px;
  grid-template-areas:"input input" "button1 button2" "average1 average2" "output1 output2";
  grid-template-columns:auto auto;
  grid-template-rows:auto auto auto auto;
  min-width:50%;

}

#number-of-tests-input {

  background-color:rgba(0, 0, 0, 0);
  border:none;
  color:#ffffff;
  font-size:1.0em;
  grid-area:input;
  text-align:center;

}

#draw-image-button {

  grid-area:button1;

}

#draw-image-average {

  grid-area:average1;

}

#draw-image-output {

  grid-area:output1;

}

#image-data-button {

  grid-area:button2;

}

#image-data-average {

  grid-area:average2;

}

#image-data-output {

  grid-area:output2;

}

.button {

  border-color:#ffffff;
  border-radius:16px;
  border-style:solid;
  border-width:1px;
  cursor:pointer;
  padding:4px;
  text-align:center;

}

.output {

  font-size:1.0em;
  height:6.0em;
  overflow-y:auto;

}


================================================
FILE: content/blit/blit.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <meta name = "description" content = "Is drawImage faster than imageData? See for yourself!">

    <link href = "blit.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Blit Test</h1>

    <p>Which method is faster: CanvasRenderingContext2D.drawImage or CanvasRenderingContext2D.putImageData?<br>Type clear to clear tests.</p>

    <canvas></canvas>

    <div>

      <input id = "number-of-tests-input" type = "text" value = "Number of tests:">
      <a class = "button" id = "draw-image-button">Test drawImage</a>
      <a class = "button" id = "image-data-button">Test imageData</a>
      <p id = "draw-image-average">Average (0 sets) 0.0 ms</p>
      <p id = "image-data-average">Average (0 sets) 0.0 ms</p>
      <p class = "output" id = "draw-image-output">Number of tests (0): 0.0 ms</p>
      <p class = "output" id = "image-data-output">Number of tests (0): 0.0 ms</p>

    </div>

    <script src = "blit.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/blit/blit.js
================================================
// Frank Poth 12/27/2017

/* This program tests drawImage and putImageData efficiency. Basically, I run these
functions thousands of times and time them. After some tests it became pretty obvious
that drawImage is way faster, at least on my version of Chrome, anyway. The speed
difference increases with the number of tests performed, but drawImage still comes
out ahead. "Comes out ahead..." What is this, a horse race? */

(function() {

  var averages, buffer, display, drawImage, image, image_context, imageData, number_of_tests, test, ui;

  averages = [[], []];// Where the averages for each set of tests are stored.
  /* I do the testing on the buffer, but to make the image bigger I draw the final
  image to the display with drawImage, which automatically scales it up. Only the
  individual drawing methods are timed, however, so don't let this distract you. */
  buffer = document.createElement("canvas").getContext("2d");
  display = document.querySelector("canvas").getContext("2d");
  image = new Image();// Where the loaded image will be stored.
  image_context = document.createElement("canvas").getContext("2d");
  number_of_tests = 10000;// The default number of tests to run.

  drawImage = function() {

    buffer.drawImage(image_context.canvas, 0, 0, image_context.canvas.width, image_context.canvas.height, 0, 0, image_context.canvas.width, image_context.canvas.height);

  };

  imageData = function() {

    buffer.putImageData(image_context.getImageData(0, 0, image_context.canvas.width, image_context.canvas.height), 0, 0);

  };

  test = function(draw) {

    /* This is the test. The time is recorded before and after the draw functions
    are called and the difference is displayed to the user. */
    let count = 0;
    let start_time = window.performance.now();

    while(count < number_of_tests) {

      count ++;

      draw();

    }

    let accumulated_time = window.performance.now() - start_time;
    let data = "Number of tests (" + number_of_tests + "): " + accumulated_time + "ms<br>";

    if (draw == drawImage) {

      ui.draw_image_output.innerHTML = data + ui.draw_image_output.innerHTML;
      averages[0].push(accumulated_time);

      let number = 0;
      for (let index = averages[0].length - 1; index > -1; -- index) {

        number += averages[0][index];

      }

      number /= averages[0].length;

      ui.draw_image_average.innerHTML = (averages[0].length > 1) ? "Average (" + averages[0].length + " sets): " + number + " ms" : "Average (" + averages[0].length + " set): " + number + " ms";

    } else if (draw == imageData) {

      ui.image_data_output.innerHTML = data + ui.image_data_output.innerHTML;
      averages[1].push(accumulated_time);

      let number = 0;
      for (let index = averages[1].length - 1; index > -1; -- index) {

        number += averages[1][index];

      }

      number /= averages[1].length;

      ui.image_data_average.innerHTML = (averages[1].length > 1) ? "Average (" + averages[1].length + " sets): " + number + " ms" : "Average (" + averages[1].length + " set): " + number + " ms";

    }

    display.imageSmoothingEnabled = false;
    display.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, display.canvas.width, display.canvas.height);

  };

  ui = {

    draw_image_average:document.getElementById("draw-image-average"),
    draw_image_button:document.getElementById("draw-image-button"),
    draw_image_output:document.getElementById("draw-image-output"),
    image_data_average:document.getElementById("image-data-average"),
    image_data_button:document.getElementById("image-data-button"),
    image_data_output:document.getElementById("image-data-output"),
    number_of_tests_input:document.getElementById("number-of-tests-input"),

    click:function(event) {

      switch(this) {

        case ui.draw_image_button:

          test(drawImage);

        break;
        case ui.image_data_button:

          test(imageData);

        break;

      }

    },

    change:function(event) {

      let number = Number.parseInt(this.value);

      if (!isNaN(number)) {

        number_of_tests = number;
        this.value = "Number of tests: (" + number + ")";

      } else if (this.value == "clear") {

        ui.draw_image_output.innerHTML = ui.image_data_output.innerHTML = "Number of tests (0): 0.0 ms";
        ui.draw_image_average.innerHTML =  ui.image_data_average.innerHTML =  "Average(0 sets): 0.0 ms";

        averages = [[], []];

        this.value = "Number of tests (" + number_of_tests + ")";

      } else {

        this.value = "Enter a valid integer";

      }

    },

    focusInOut:function(event) {

      switch(event.type) {

        case "focusin":

          this.value = "";

        break;
        case "focusout":

          if (this.value == "") this.value = "Number of tests (" + number_of_tests + ")";

      }

    }

  };

  //// INITIALIZE ////

  image.addEventListener("load", function(event) {

    buffer.canvas.height = this.height;
    buffer.canvas.width = this.width;
    display.canvas.height = this.height * 2;
    display.canvas.width = this.width * 2;

    image_context.canvas.height = this.height;
    image_context.canvas.width = this.width;

    image_context.drawImage(this, 0, 0, this.width, this.height, 0, 0, this.width, this.height);

    ui.draw_image_button.addEventListener("click", ui.click);
    ui.image_data_button.addEventListener("click", ui.click);

    ui.number_of_tests_input.addEventListener("focusin", ui.focusInOut);
    ui.number_of_tests_input.addEventListener("focusout", ui.focusInOut);
    ui.number_of_tests_input.addEventListener("change", ui.change);
    ui.number_of_tests_input.value = "Number of tests (" + number_of_tests + ")";

  });

  image.src = "blit.png";

})();


================================================
FILE: content/bouncing-polygons/bouncing-polygons.html
================================================
<!DOCTYPE HTML>

<html>

  <head>

    <meta name = "description" content = "Get the bounding rectangle of an arbitrary polygon.">
    <meta name = "viewport" content = "width=device-width, user-scalable=no">

    <title>Bouncing Polygons</title>

    <style>

      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }

      body, html { height:100%; width:100%; }

      body { background-color:#202830; display:grid; }

      canvas { align-self:center; display:grid; justify-self:center; }
    
    </style>

  </head>

  <body>

    <canvas></canvas>

    <script type = "text/javascript">
    
        /////////////////
       //// CLASSES ////
      /////////////////

      /* The bottom boundary will be used to show how useful it is to know how to get the farthest point along a given direction vector. The polygons will be able to collide with this non-axis aligned surface with pixel perfect precision. */
      class BottomBoundary {

        constructor() {

          this.line_segment = new LineSegment2D(0, 0, 0, 0, 0, 0); // The top surface
          this.minimum_y = undefined; // The highest the points on the segment can go.
          this.middle_y  = undefined; // The middle of the two points on the segment
          this.maximum_y = undefined; // The lowest the points on the segment can go.
          this.tilt_speed = 0.25;

        }

        /* Tilt the line segment between minimum and maximum y. */
        update() {

          this.line_segment.point0.y += this.tilt_speed;
          this.line_segment.point1.y = this.middle_y - this.line_segment.point0.y + this.middle_y;

          if (this.line_segment.point0.y < this.minimum_y) {

            this.line_segment.point0.y = this.minimum_y;
            this.tilt_speed = -this.tilt_speed;

          } else if (this.line_segment.point0.y > this.maximum_y) {

            this.line_segment.point0.y = this.maximum_y;
            this.tilt_speed = -this.tilt_speed;

          }

        }

      }

      /* This line segment class will be used to make the bottom_boundary object. It's also a nice simple line segment class if you want to use it. */
      class LineSegment2D {

        constructor(x, y, x0, y0, x1, y1) {

          this.position = new Point2D(x, y);

          this.point0 = new Point2D(x0, y0);
          this.point1 = new Point2D(x1, y1);

        }

      }

      class Point2D {

        constructor(x, y) {

          this.x = x; this.y = y;

        }

        /* Moves the point to the specified x and y coordinates. */
        setPosition(x, y) {

          this.x = x; this.y = y;

        }

      }

      class Polygon2D {

        constructor(x, y, ...vertices) {
          
          this.position = new Point2D(x, y);
          this.rotation = 0;
          this.scale = 1;
          this.vertices = new Array(); // points in the polygon

          /* Have some extra values to make the polygons move around. */
          this.direction = Math.random() * Math.PI * 2;
          this.speed     = Math.random() * 2 + 1;
          this.rotational_velocity = Math.random() * 0.05 - 0.025;

          for (let index = vertices.length - 2; index > -1; index -= 2) {

            this.vertices[index * 0.5] = new Point2D(vertices[index], vertices[index + 1]);

          }
        
        }

        /* Get the y value of the point in the polygon that is farthest down on the y axis. */
        getBottom() {

          var vertex = this.vertices[0];
          var bottom = vertex.y;

          for (let index = this.vertices.length - 1; index > 0; -- index) {

            vertex = this.vertices[index];

            if (vertex.y > bottom) bottom = vertex.y;

          } return bottom;

        }

        /* Get the x value of the point in the polygon that is farthest left on the x axis. */
        getLeft() {

          var vertex = this.vertices[0];
          var left   = vertex.x;

          for (let index = this.vertices.length - 1; index > 0; -- index) {

            vertex = this.vertices[index];

            if (vertex.x < left) left = vertex.x;

          } return left;

        }

        /* Get the point in the polygon that is farthest along the given vector. */
        getFarthestPoint(vector_x, vector_y) {

          var vertex        = this.vertices[0]; // Start with one of the vertices
          var dot_product   = vertex.x * vector_x + vertex.y * vector_y; // Dot product is used to determine how far along the vector this point is. The bigger dot product is, the farther along the vector the point is.
          var farthest_point = new Point2D(vertex.x, vertex.y); // Since we have only checked one point so far, we can assume it is the farthest along the vector.

          // loop through all points except for the one we already checked.
          for (let index = this.vertices.length - 1; index > 0; -- index) {

            vertex = this.vertices[index];

            let test_dot_product = vertex.x * vector_x + vertex.y * vector_y;

            // if the test dot product is bigger than the one for the last point we checked, we know this point is farther along the vector.
            if (test_dot_product > dot_product) {

              dot_product = test_dot_product; // set up for next iteration
              farthest_point.x = vertex.x; // set up our new farthest point
              farthest_point.y = vertex.y;

            }

          } return farthest_point;

        }

        /* Get the x value of the point in the polygon that is farthest right on the x axis. */
        getRight() {

          var vertex = this.vertices[0];
          var right  = vertex.x;

          for (let index = this.vertices.length - 1; index > 0; -- index) {

            vertex = this.vertices[index];

            if (vertex.x > right) right = vertex.x;

          } return right;

        }

        /* Get the y value of the point in the polygon that is farthest up on the y axis. */
        getTop() {

          var vertex = this.vertices[0];
          var top    = vertex.y;

          for (let index = this.vertices.length - 1; index > 0; -- index) {

            vertex = this.vertices[index];

            if (vertex.y < top) top = vertex.y;

          } return top;

        }

        setPosition(x, y) {

          var vector_x = x - this.position.x;
          var vector_y = y - this.position.y;

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let vertex = this.vertices[index];
            
            vertex.x += vector_x;
            vertex.y += vector_y;

          }

          this.position.x += vector_x;
          this.position.y += vector_y;

        }

        /* This just adds x and y to every point in the polygon as well as to its position. It's the equivalent of a slide function. */
        setPositionPlus(x, y) {

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let vertex = this.vertices[index];

            vertex.x += x;
            vertex.y += y;

          }

          this.position.x += x;
          this.position.y += y;

        }

        setRotation(rotation) {

          var radians = rotation - this.rotation;
          var unit_x = Math.cos(radians);
          var unit_y = Math.sin(radians);

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let vertex = this.vertices[index];
            
            let vector_x = vertex.x - this.position.x;
            let vector_y = vertex.y - this.position.y;

            vertex.x = vector_x * unit_x - vector_y * unit_y + this.position.x;
            vertex.y = vector_x * unit_y + vector_y * unit_x + this.position.y;

          }

          this.rotation = rotation;

        }

        setScale(scale) {

          var ratio = scale / this.scale;

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let vertex = this.vertices[index];

            let vector_x = vertex.x - this.position.x;
            let vector_y = vertex.y - this.position.y;

            vertex.x = vector_x * ratio + this.position.x;
            vertex.y = vector_y * ratio + this.position.y;

          }

          this.scale = scale;

        }

        update() {

          this.setPositionPlus(Math.cos(this.direction) * this.speed, Math.sin(this.direction) * this.speed);

        }

      }

      /* This is the same as the Point2D class except it has vector math functionality in its prototype. */
      class Vector2D {

        constructor(x, y) {

          this.x = x; this.y = y;

        }

        /* Returns a vector that runs from point0 to point1. */
        static fromPoints(point0, point1) {

          return new Vector2D(point1.x - point0.x, point1.y - point0.y);

        }

        /* Clones the parameter vector. */
        static fromVector(vector2d) {

          return new Vector2D(vector2d.x, vector2d.y);

        }

        /* This tells us if a vector is to the left or right of another vector by returning a positive number if it is to the left of the other vector and a negative number if it is to the right of the other vector. */
        getCrossProduct(vector2d) {

          return this.x * vector2d.y - this.y * vector2d.x;

        }

        /* We don't use this, but I figured I'd leave it in for reference. */
        toLeftNormal() {

          var x = this.x;

          this.x = this.y;
          this.y = -x;

        }

        /* Imaging taking a vector and rotating it 90 degrees clockwise. That is essentially what this function does. toLeftNormal turns a vector 90 degrees counter-clockwise. */
        toRightNormal() {
          
          var x = this.x;

          this.x = -this.y;
          this.y = x;

        }

      }

        ///////////////////
       //// VARIABLES ////
      ///////////////////

      var context = document.querySelector("canvas").getContext("2d", { alpha:false });

      var polygons = [new Polygon2D(0, 0, 0, -1, 1, 0, 0.5, 0, 0.5, 1, -0.5, 1, -0.5, 0, -1, 0),
                  new Polygon2D(0, 0, -0.25, -1, -0.25, -0.25, 1, -0.25, 0.25, 1, 0.25, 0.25, -1, 0.25),
                  new Polygon2D(0, 0, -1, 0, -0.5, -0.75, 0.5, -0.75, 1, 0, 0.5, 0.75, -0.5, 0.75),
                  new Polygon2D(0, -0.25, -1, -0.5, 0, -1.5, 1, -0.5, 0.75, 0.75, -0.75, 0.75),
                  new Polygon2D(0, 0, -1, -1, 1, -1, 1, 1, -1, 1),
                  new Polygon2D(0, 0.25, 0, -1, 1, 1, -1, 1)];

      var bottom_boundary = new BottomBoundary();


        ///////////////////
       //// FUNCTIONS ////
      ///////////////////

      /* Essentially what is happening here is we're getting the vector of the line segment, ab. Then we're getting the right hand normal, abn, of the line segment. Then we're using the normal to find c, which is the point on the polygon that penetrates deepest below the line segment along the normal, abn. Once we have c we can find the vector from c to a, vector ca. We will test the cross product of ca and ab to determine if c has in fact penetrated below the line segment. If it has, then we must find the penetration vector, cd, that lies along abn and move the polygon back by its amount to resolve collision. */
      function collideBottomBoundary(polygon) {

        var ab = Vector2D.fromPoints(bottom_boundary.line_segment.point0, bottom_boundary.line_segment.point1);// ab = segment vector
        
        var abn = Vector2D.fromVector(ab); // clone ab
        abn.toRightNormal(); // convert it to the right hand normal

        var c = polygon.getFarthestPoint(abn.x, abn.y);

        var ca = Vector2D.fromPoints(c, bottom_boundary.line_segment.point0);

        if (ca.getCrossProduct(ab) > 0) { // c is below the line segment!

          // This equation to find the factor will help us find cd.
          var factor = (-ab.x * ca.x - ab.y * ca.y) / (ab.x * ab.x + ab.y * ab.y);

          var cd = new Vector2D(ca.x + factor * ab.x, ca.y + factor * ab.y);

          polygon.setPositionPlus(cd.x, cd.y); // move c to d along with the rest of the polygon.
          polygon.direction = Math.atan2(Math.sin(polygon.direction) * -1, Math.cos(polygon.direction)); // reverse the polygon's direction on the y axis.

        }        

      }

      /* What is happening here is we are getting the farthest points in the polygon in axis aligned directions. Then we are checking to see if those farthest points are beyond the limits of the boundary. If they are, we move the polygon back. */
      function collideRectangularContainer(polygon, left, right, top) {

        var polygon_left   = polygon.getLeft();
        var polygon_right  = polygon.getRight();
        var polygon_top    = polygon.getTop();

        if (polygon_left < left) {

          polygon.setPositionPlus(left - polygon_left, 0);
          polygon.direction = Math.atan2(Math.sin(polygon.direction), Math.cos(polygon.direction) * -1);

        } else if (polygon_right > right) {

          polygon.setPositionPlus(right - polygon_right, 0);
          polygon.direction = Math.atan2(Math.sin(polygon.direction), Math.cos(polygon.direction) * -1);

        }

        if (polygon_top < top) {

          polygon.setPositionPlus(0, top - polygon_top);
          polygon.direction = Math.atan2(Math.sin(polygon.direction) * -1, Math.cos(polygon.direction));

        }

        // No need for bottom collision because of the bottom boundary.

      }

      function drawBottomBoundary(color) {

        context.beginPath();
        context.moveTo(bottom_boundary.line_segment.point0.x, bottom_boundary.line_segment.point0.y);
        context.lineTo(bottom_boundary.line_segment.point1.x, bottom_boundary.line_segment.point1.y);
        context.lineTo(context.canvas.width, context.canvas.height);
        context.lineTo(0, context.canvas.height);

        context.fillStyle = color;
        context.fill();

      }

      function drawPolygon2D(polygon2d, color) {

        var vertex = polygon2d.vertices[0];

        context.beginPath();
        context.moveTo(vertex.x, vertex.y);

        for (let index = polygon2d.vertices.length - 1; index > 0; -- index) {

          vertex = polygon2d.vertices[index];
          context.lineTo(vertex.x, vertex.y);

        }

        context.fillStyle = color;
        context.fill();

      }

      function loop(time_stamp) {

        window.requestAnimationFrame(loop);

        context.fillStyle = "#c0f0d8";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        bottom_boundary.update();

        drawBottomBoundary("rgba(0,128,256,0.75");

        for (let index = polygons.length - 1; index > -1; -- index) {

          var polygon = polygons[index];

          polygon.update();
          polygon.setRotation(polygon.rotation + polygon.rotational_velocity);

          collideRectangularContainer(polygon, 0, context.canvas.width, 0);

          collideBottomBoundary(polygon);

          drawPolygon2D(polygon, "rgba(0,128,256,0.75)");

        }

        // Draw an Axis Aligned bounding box
        context.beginPath();
        context.rect(polygon.getLeft(), polygon.getTop(), polygon.getRight() - polygon.getLeft(), polygon.getBottom() - polygon.getTop());
        context.strokeStyle = "rgba(0,128,256,0.75)";
        context.stroke();

      }

        /////////////////////////
       //// EVENT LISTENERS ////
      /////////////////////////

      function resize(event) {

        context.canvas.height = document.documentElement.clientHeight - 16;
        context.canvas.width = document.documentElement.clientWidth - 16;

        context.imageSmoothingEnabled = false;

        bottom_boundary.middle_y = context.canvas.height * 0.8;
        bottom_boundary.minimum_y = context.canvas.height * 0.6;
        bottom_boundary.maximum_y = context.canvas.height;
        bottom_boundary.line_segment.point0.setPosition(0, bottom_boundary.middle_y);
        bottom_boundary.line_segment.point1.setPosition(context.canvas.width, bottom_boundary.middle_y);

      }

        ////////////////////
       //// INITIALIZE ////
      ////////////////////

      window.addEventListener("resize", resize);

      resize();

      /* Move the polygons to the middle of the canvas and resize them. */
      for (let index = polygons.length - 1; index > -1; -- index) {

        let polygon = polygons[index];

        polygon.setPosition(context.canvas.width * 0.5, context.canvas.height * 0.5);
        polygon.setScale(Math.floor(Math.random() * 30) + 20);

      }

      /* Randomly sort the array of polygons so a different polygon can be drawn with a bounding box each time the program runs. */
      for (let index = polygons.length - 1; index > -1; -- index) {

        let polygon = polygons.pop();

        polygons.splice(Math.floor(Math.random() * polygons.length - 1), 0, polygon);
        
      }

      loop(); // start the game loop.

    </script>

  </body>

</html>

================================================
FILE: content/calculator/calculator.css
================================================
/* Frank Poth 12/29/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;
  user-select:none;

}

html {

  --f1-color:#003399;
  --f2-color:#0066cc;
  --f3-color:#0099ff;

  height:100%; width:100%;

}

body {

  align-content:center;
  background-color:#202830;
  display:grid;
  grid-template-columns:100%;
  grid-template-rows:100%;
  justify-content:center;
  min-height:100%;
  width:100%;

}

#calculator {

  align-self:center;
  background-color:#999999;
  border-radius:16px 16px 8px 8px;
  display:grid;
  grid-column-gap:4px;
  grid-row-gap:4px;
  grid-template-areas:"screen screen screen screen screen"
                      "q      clr    f1     f2     f3"
                      "n1     n2     n3     cmd1   cmd2"
                      "n4     n5     n6     cmd3   cmd4"
                      "n7     n8     n9     cmd5   cmd6"
                      "cma    n0     prd    cmd7   cmd8";
  grid-template-columns:min-content min-content min-content min-content min-content;
  grid-template-rows:2fr 1fr 1fr 1fr 1fr 1fr;
  justify-self:center;
  max-width:100%;
  overflow:hidden;
  padding:4px;

}

#calculator-screen {

  align-items:center;
  background-color:#ffffff;
  border-radius:16px 16px 8px 8px;
  color:#202830;
  display:grid;
  font-size:2.0em;
  grid-area:screen;
  height:128px;
  overflow-y:auto;
  padding:4px 8px;
  text-align:center;
  white-space:wrap;
  word-wrap:break-word;
  word-break:break-all;
  user-select:text;

}

#calculator-q        { background-color:#ff9900; grid-area:q; }
#calculator-clr      { background-color:#990000; grid-area:clr; }
#calculator-f1       { background-color:var(--f1-color); grid-area:f1; }
#calculator-f2       { background-color:var(--f2-color); grid-area:f2; }
#calculator-f3       { background-color:var(--f3-color); grid-area:f3; }
#calculator-0        { grid-area:n0;   }
#calculator-1        { grid-area:n1;   }
#calculator-2        { grid-area:n2;   }
#calculator-3        { grid-area:n3;   }
#calculator-4        { grid-area:n4;   }
#calculator-5        { grid-area:n5;   }
#calculator-6        { grid-area:n6;   }
#calculator-7        { grid-area:n7;   }
#calculator-8        { grid-area:n8;   }
#calculator-9        { grid-area:n9;   }
#calculator-cma      { grid-area:cma;  }
#calculator-prd      { grid-area:prd;  }
#calculator-plus     { background-color:var(--f1-color); grid-area:cmd1; }
#calculator-minus    { background-color:var(--f1-color); grid-area:cmd2; }
#calculator-multiply { background-color:var(--f1-color); grid-area:cmd3; }
#calculator-divide   { background-color:var(--f1-color); grid-area:cmd4; }
#calculator-open-parenthesis { background-color:var(--f1-color); grid-area:cmd5; }
#calculator-close-parenthesis { background-color:var(--f1-color); grid-area:cmd6; }
#calculator-pi { background-color:var(--f2-color); grid-area:cmd1; display:none; }
#calculator-pow { background-color:var(--f2-color); grid-area:cmd2; display:none; }
#calculator-sqrt { background-color:var(--f2-color); grid-area:cmd3; display:none; }
#calculator-ln { background-color:var(--f2-color); grid-area:cmd4; display:none; }
#calculator-log { background-color:var(--f2-color); grid-area:cmd5; display:none; }
#calculator-rnd { background-color:var(--f2-color); grid-area:cmd6; display:none; }
#calculator-cos { background-color:var(--f3-color); grid-area:cmd1; display:none; }
#calculator-sin { background-color:var(--f3-color); grid-area:cmd2; display:none; }
#calculator-tan { background-color:var(--f3-color); grid-area:cmd3; display:none; }
#calculator-acos { background-color:var(--f3-color); grid-area:cmd4; display:none; }
#calculator-asin { background-color:var(--f3-color); grid-area:cmd5; display:none; }
#calculator-atan { background-color:var(--f3-color); grid-area:cmd6; display:none; }

#calculator-del      { background-color:#990000; grid-area:cmd7; }
#calculator-ans      { background-color:#009900; grid-area:cmd8; }

.calculator-button {

  align-items:center;
  background-color:#303840;
  border-radius:8px;
  color:#ffffff;
  cursor:pointer;
  display:grid;
  font-size:1.5em;
  height:64px;
  justify-items:center;
  width:64px;

}

.calculator-button:hover {

  background-color:#384048;

}


================================================
FILE: content/calculator/calculator.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "initial-scale=1,user-scalable=no,width=device-width">
    <meta name = "description" content = "A simple calculator written in JavaScript by Frank Poth.">
    <link href = "calculator.css" rel = "stylesheet" type = "text/css">

    <title>Calculator</title>

  </head>

  <body>

    <div id = "calculator">

      <div id = "calculator-screen"></div>

      <div class = "calculator-button" id = "calculator-clr">clr</div>
      <div class = "calculator-button" id = "calculator-f1">f1</div>
      <div class = "calculator-button" id = "calculator-f2">f2</div>
      <div class = "calculator-button" id = "calculator-f3">f3</div>

      <div class = "calculator-button" id = "calculator-0">0</div>
      <div class = "calculator-button" id = "calculator-1">1</div>
      <div class = "calculator-button" id = "calculator-2">2</div>
      <div class = "calculator-button" id = "calculator-3">3</div>
      <div class = "calculator-button" id = "calculator-4">4</div>
      <div class = "calculator-button" id = "calculator-5">5</div>
      <div class = "calculator-button" id = "calculator-6">6</div>
      <div class = "calculator-button" id = "calculator-7">7</div>
      <div class = "calculator-button" id = "calculator-8">8</div>
      <div class = "calculator-button" id = "calculator-9">9</div>

      <div class = "calculator-button" id = "calculator-plus">+</div>
      <div class = "calculator-button" id = "calculator-minus">-</div>
      <div class = "calculator-button" id = "calculator-multiply">*</div>
      <div class = "calculator-button" id = "calculator-divide">/</div>
      <div class = "calculator-button" id = "calculator-open-parenthesis">(</div>
      <div class = "calculator-button" id = "calculator-close-parenthesis">)</div>
      <div class = "calculator-button" id = "calculator-pi">PI</div>
      <div class = "calculator-button" id = "calculator-pow">pow(</div>
      <div class = "calculator-button" id = "calculator-sqrt">sqrt(</div>
      <div class = "calculator-button" id = "calculator-ln">ln(</div>
      <div class = "calculator-button" id = "calculator-log">log(</div>
      <div class = "calculator-button" id = "calculator-rnd">rnd()</div>

      <div class = "calculator-button" id = "calculator-cos">cos(</div>
      <div class = "calculator-button" id = "calculator-sin">sin(</div>
      <div class = "calculator-button" id = "calculator-tan">tan(</div>
      <div class = "calculator-button" id = "calculator-acos">acos(</div>
      <div class = "calculator-button" id = "calculator-asin">asin(</div>
      <div class = "calculator-button" id = "calculator-atan">atan(</div>

      <div class = "calculator-button" id = "calculator-prd">.</div>
      <div class = "calculator-button" id = "calculator-cma">,</div>
      <div class = "calculator-button" id = "calculator-ans">ans</div>
      <div class = "calculator-button" id = "calculator-del">del</div>

      <div class = "calculator-button" id = "calculator-q">?</div>

    </div><!-- calculator -->

    <script src = "calculator.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/calculator/calculator.js
================================================
// Frank Poth 12/29/2017

(function() { "use strict";

  const PI = Math.PI;
  const pow = Math.pow;
  const sqrt = Math.sqrt;
  const ln = Math.log;
  const log = Math.log10;
  const rnd = Math.random;
  const cos = Math.cos;
  const sin = Math.sin;
  const tan = Math.tan;
  const acos = Math.acos;
  const asin = Math.asin;
  const atan = Math.atan;

  var controller, resize, ui, update;

  controller = {

    active:false, state:false, value:"",

    click:function(event) {

      controller.active = true;
      controller.value = this.innerHTML;

      update();

    },

    keyPress:function(event) {

      if (!event.repeat) {

        controller.active = true;
        controller.value = String(event.key);

      }

      update();

    }

  };

  ui = {

    calculator:document.getElementById("calculator"),
    screen:document.getElementById("calculator-screen"),

    buttons: {

      "?":document.getElementById("calculator-q"),
      "clr":document.getElementById("calculator-clr"),
      "f1":document.getElementById("calculator-f1"),
      "f2":document.getElementById("calculator-f2"),
      "f3":document.getElementById("calculator-f3"),

      "0":document.getElementById("calculator-0"),
      "1":document.getElementById("calculator-1"),
      "2":document.getElementById("calculator-2"),
      "3":document.getElementById("calculator-3"),
      "4":document.getElementById("calculator-4"),
      "5":document.getElementById("calculator-5"),
      "6":document.getElementById("calculator-6"),
      "7":document.getElementById("calculator-7"),
      "8":document.getElementById("calculator-8"),
      "9":document.getElementById("calculator-9"),

      "+":document.getElementById("calculator-plus"),
      "-":document.getElementById("calculator-minus"),
      "/":document.getElementById("calculator-divide"),
      "*":document.getElementById("calculator-multiply"),
      "(":document.getElementById("calculator-open-parenthesis"),
      ")":document.getElementById("calculator-close-parenthesis"),
      "PI":document.getElementById("calculator-pi"),
      "pow(":document.getElementById("calculator-pow"),
      "sqrt(":document.getElementById("calculator-sqrt"),
      "cos(":document.getElementById("calculator-cos"),
      "sin(":document.getElementById("calculator-sin"),
      "tan(":document.getElementById("calculator-tan"),
      "acos(":document.getElementById("calculator-acos"),
      "asin(":document.getElementById("calculator-asin"),
      "atan(":document.getElementById("calculator-atan"),
      "ln(":document.getElementById("calculator-ln"),
      "log(":document.getElementById("calculator-log"),
      "rnd()":document.getElementById("calculator-rnd"),
      ",":document.getElementById("calculator-cma"),
      ".":document.getElementById("calculator-prd"),
      "del":document.getElementById("calculator-del"),
      "ans":document.getElementById("calculator-ans"),

    },

    hitF1:function() {

      this.buttons["+"].style.display = "grid";
      this.buttons["-"].style.display = "grid";
      this.buttons["*"].style.display = "grid";
      this.buttons["/"].style.display = "grid";
      this.buttons["("].style.display = "grid";
      this.buttons[")"].style.display = "grid";

      this.buttons["PI"].style.display = "none";
      this.buttons["pow("].style.display = "none";
      this.buttons["sqrt("].style.display = "none";
      this.buttons["ln("].style.display = "none";
      this.buttons["log("].style.display = "none";
      this.buttons["rnd()"].style.display = "none";
      this.buttons["cos("].style.display = "none";
      this.buttons["sin("].style.display = "none";
      this.buttons["tan("].style.display = "none";
      this.buttons["acos("].style.display = "none";
      this.buttons["asin("].style.display = "none";
      this.buttons["atan("].style.display = "none";

    },

    hitF2:function() {

      this.buttons["+"].style.display = "none";
      this.buttons["-"].style.display = "none";
      this.buttons["*"].style.display = "none";
      this.buttons["/"].style.display = "none";
      this.buttons["("].style.display = "none";
      this.buttons[")"].style.display = "none";
      this.buttons["cos("].style.display = "none";
      this.buttons["sin("].style.display = "none";
      this.buttons["tan("].style.display = "none";
      this.buttons["acos("].style.display = "none";
      this.buttons["asin("].style.display = "none";
      this.buttons["atan("].style.display = "none";

      this.buttons["PI"].style.display = "grid";
      this.buttons["pow("].style.display = "grid";
      this.buttons["sqrt("].style.display = "grid";
      this.buttons["ln("].style.display = "grid";
      this.buttons["log("].style.display = "grid";
      this.buttons["rnd()"].style.display = "grid";

    },

    hitF3:function() {

      this.buttons["+"].style.display = "none";
      this.buttons["-"].style.display = "none";
      this.buttons["*"].style.display = "none";
      this.buttons["/"].style.display = "none";
      this.buttons["("].style.display = "none";
      this.buttons[")"].style.display = "none";
      this.buttons["PI"].style.display = "none";
      this.buttons["pow("].style.display = "none";
      this.buttons["sqrt("].style.display = "none";
      this.buttons["ln("].style.display = "none";
      this.buttons["log("].style.display = "none";
      this.buttons["rnd()"].style.display = "none";

      this.buttons["cos("].style.display = "grid";
      this.buttons["sin("].style.display = "grid";
      this.buttons["tan("].style.display = "grid";
      this.buttons["acos("].style.display = "grid";
      this.buttons["asin("].style.display = "grid";
      this.buttons["atan("].style.display = "grid";

    }

  };

  resize = function(event) {

    let height = Math.floor(document.documentElement.clientHeight);
    let width = Math.floor(document.documentElement.clientWidth);
    ui.screen.style.maxWidth = ui.screen.clientWidth + "px";

  };

  update = function() {

    if (controller.active) {

      controller.active = false;

      switch(controller.value) {

        case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
        case "+": case "-": case "/": case "*": case "(": case ")":
        case "PI": case "pow(": case "sqrt(": case "ln(": case "log(": case "rnd()":
        case "cos(": case "sin(": case "tan(": case "acos(": case "asin(": case "atan(":
        case ",": case ".":

          ui.screen.innerHTML += controller.value;

        break;

        case "clr": ui.screen.innerHTML = ""; break;
        case "f1": ui.hitF1(); break;
        case "f2": ui.hitF2(); break;
        case "f3": ui.hitF3(); break;

        case "del": case "Delete":

          if (ui.screen.innerHTML.length > 0) ui.screen.innerHTML = ui.screen.innerHTML.slice(0, ui.screen.innerHTML.length - 1);

        break;

        case "Enter": case "ans":

          let answer = undefined;

          try {

            answer = parseFloat(eval(ui.screen.innerHTML).toPrecision(10));

          } catch(error) {

            answer = error;

          }

          ui.screen.innerHTML = answer;

        break;

        case "?": ui.screen.innerHTML = "Help Menu<br><br>*Always close parenthesis.<br><br>pow(<br>Returns the base to the exponent power. Must be written in the form pow(base, exponent)<br><br>acos( & asin(<br> Return the arcCosine and arcTangent of a number in radians. The number must be between -1 and 1 or NaN will be returned.<br><br>ln( & log(<br>ln is base e. log is base 10.<br><br>rnd()<br>Returns a pseudo random number.<br><br>This is a Javascript calculator that evaluates input with the eval method. This application was written by Frank Poth.";

      }

    }

  };

      ////////////////////
    //// INITIALIZE ////
  ////////////////////

  window.addEventListener("resize", resize);

  for (let property in ui.buttons) {

    ui.buttons[property].addEventListener("click",  controller.click, { passive:true });

  }

  window.addEventListener("keypress", controller.keyPress);

  resize();

})();


================================================
FILE: content/canvas/canvas.css
================================================
/* Frank Poth 08/04/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

body, html {

  width:100%;
  height:100%;

}

body {

  align-content:space-around;
  background-color:#202430;
  color:#ffffff;
  display:grid;
  font-family:monospace;
  justify-items:center;
  padding:8px;

}

canvas {

  background-color:#ffffff;

}


================================================
FILE: content/canvas/canvas.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <link href = "canvas.css" rel = "stylesheet" type = "text/css">
    <title>PoP Vlog - Canvas</title>

  </head>

  <body>

    <h1>PoP Vlog - Canvas</h1>

    <canvas id = "display"></canvas>

    <script src = "canvas.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/canvas/canvas.js
================================================
// Frank Poth 08/04/2017

var display = document.getElementById("display").getContext("2d");

display.canvas.height = 180;
display.canvas.width = 320;

display.fillStyle = "#008000";
display.fillRect(0, 0, 320, 180);

display.strokeStyle = "#ffffff";
display.lineJoin = "round";
display.lineWidth = 4;

display.fillStyle = "#00ff00";
display.beginPath();
display.moveTo(10, 10);
display.lineTo(10, 90);
display.lineTo(90, 10);
display.closePath();
display.fill();
display.stroke();

display.beginPath();
display.moveTo(0, 180);
display.bezierCurveTo(80, 0, 80, 180, 160, 90);
display.bezierCurveTo(240, 0, 240, 180, 320, 0);
display.stroke();

display.fillStyle = "#0000ff";
display.beginPath();
display.rect(180, 130, 40, 40);
display.fill();
display.stroke();

display.fillStyle = "#ff0000";
display.beginPath();
display.arc(290, 150, 20, 0, Math.PI*2);
display.fill();
display.stroke();


================================================
FILE: content/circle-collision-detection/circle-collision-detection.html
================================================
<!DOCTYPE html>

<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision
detection. To determine if two circles are colliding you must check to see if the
distance between them is less than the sum of their radii. The formula for distance
is: sqrt((x1 - x2)^2 + (y1 - y2)^2) So the check would look like:

if (sqrt((x1 - x2)^2 + (y1 - y2)^2) <= radius1 + radius2) return true; // Collision

Since sqrt is an expensive operation, we can instead check the squared distance
against the squared sum of the radii:

if ((x1 - x2)^2 + (y1 - y2)^2 <= (radius1 + radius2)^2) return true; // Collision

And that's how you do circle collision. -->

<html>

  <head>

    <meta name = "viewport"    content = "width=device-width">
    <meta name = "description" content = "Learn circle collision detection from pure JavaScript source code!">

    <title>Circle Collision Detection</title>

    <style>

      * { margin:0; padding:0; box-sizing:border-box; }

      html { height:100%; width:100%; }

      body {

        background-color:#202830;
        display:grid;
        grid-row-gap:16px;
        grid-template-columns:100%;
        grid-template-rows:min-content auto;
        height:100%;
        justify-items:center;
        padding:16px;
        width:100%;

      }

      p { color:#ffffff; font-size:1.25em; max-width:680px; }

      a { color:#f08000; cursor:pointer; display:inline-block; font-weight:600; user-select:none; }

      canvas { height:100%; max-width:680px; width:100%; }

    </style>

  </head>

  <body>

    <p>Two circles collide when the distance between their center points is less
    than or equal to the sum of their radii. In this example, the distance between
    circles is represented by the white line. Its length is <span id = "length" style = "display:inline-block;">0</span>.
    the sum of the radii is <span id = "sum_radii" style = "display:inline-block;">0</span>. <a>change radii</a></p>
    <canvas></canvas>

    <script type = "text/javascript">

    // Frank Poth 04/15/2018

      /* The Circle class defines a simple circle with its center point at x, y. */
      const Circle = function(x, y, radius) {

        this.x      = x;
        this.y      = y;
        this.radius = radius;

      };

      /* Fires when you click the "change radii" <a> tag. */
      function changeRadii(event) {

        circle1.radius = Math.floor(Math.random() * 41 + 10);
        circle2.radius = Math.floor(Math.random() * 41 + 10);

        update();

      };

      /* To test if two circles are overlapping, you simply test to see if the distance
      between their two center points is less than their combined radii. The formula
      for distance between two points is sqrt((p1.x - p2.x)^2 + (p1.y - p2.y)^2), but
      since sqrt is an expensive operation, we can simply stop after we square the two
      differences and test against the square of the sum of the radii. */
      function collideCircle(circle1, circle2) {

        /* first we get the x and y distance between the two circles. */
        let distance_x = circle1.x      - circle2.x;
        let distance_y = circle1.y      - circle2.y;
        /* Then we get the sum of their radii. */
        let radii_sum  = circle1.radius + circle2.radius;

        /* Then we test to see if the square of their distance is greater than the
        square of their radii. If it is, then there is no collision. If it isn't,
        then we have a collision. */
        if (distance_x * distance_x + distance_y * distance_y <= radii_sum * radii_sum) return true;

        return false;

      }

      /* Fills the specified circle with the specified color. */
      function fillCircle(circle, color) {

        context.fillStyle = color;
        context.beginPath();
        //arc(centerX, centerY, radius, start_radian, stop_radian);
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
        context.fill();

      }

      /* Draws the boundary of the specified circle with the specified color. */
      function strokeCircle(circle, color) {

        context.strokeStyle = color;
        context.beginPath();
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
        context.stroke();

      }

      /* Called whenever user input is entered via mouse or touch. */
      function handleInput(event) {

        /* Calling event.preventDefault is important so we don't scroll or scale
        when we interact with the canvas. We can call it because the event listener's
        passive property is set to false. */
        event.preventDefault();

        /* We need to know where the canvas is located in the browser window to
        get the offset position to subtract from the input location. */
        let canvas_rectangle = context.canvas.getBoundingClientRect();
        let x = undefined;
        let y = undefined;

        switch(event.type) {

          /* For any touch event, we must get the client position from the targetTouches array. */
          case "touchend": case "touchmove": case "touchstart":

          x = event.targetTouches[0].clientX;
          y = event.targetTouches[0].clientY;

          break;
          /* The default would be mouse events, and these just use clientX and clientY. */
          default:

          x = event.clientX;
          y = event.clientY;

        }

        /* Set circle1's position equal to the input position offset by the bounding rect of the canvas. */
        circle1.x = x - canvas_rectangle.left;
        circle1.y = y - canvas_rectangle.top;

        update();// draw everything to the screen

      }

      /* Here we draw everything and we also test for collision with the collideCircle method. */
      function update() {

        /* Keep circle2 in the center of the canvas. */
        circle2.x = context.canvas.clientWidth  * 0.5;
        circle2.y = context.canvas.clientHeight * 0.5;

        let collision = false;

        if (collideCircle(circle1, circle2)) {// collision!

          collision = true;

        }

        /* Set the output values. */
        length.innerHTML = Math.round(Math.sqrt(Math.pow(circle1.x - circle2.x, 2) + Math.pow(circle1.y - circle2.y, 2)));
        length.style.width = length.clientWidth + "px";
        sum_radii.innerHTML = circle1.radius + circle2.radius;
        sum_radii.style.width = sum_radii.clientWidth + "px";

        /* We have to reset the canvas height and width in case the p element
        gets resized. If it does the css will adjust the canvas height and skew
        the image inside. To combat this, we just get the computed width and height
        on each update. */
        context.canvas.height = context.canvas.clientHeight;
        context.canvas.width = context.canvas.clientWidth;

        /* Clear the canvas to gray. */
        context.fillStyle = "#283038";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        if (collision) {

          strokeCircle(circle1, "#ffffff");
          strokeCircle(circle2, "#ffffff");

        } else {

          fillCircle(circle1, "#0080f0");
          fillCircle(circle2, "#f08000");

        }

        /* Draw the white line. */
        context.strokeStyle = "#ffffff";
        context.beginPath();
        context.moveTo(circle1.x, circle1.y);
        context.lineTo(circle2.x, circle2.y);
        context.stroke();

      };

      var circle1, circle2, context, length, sum_radii;

      circle1   = new Circle(100, 100, 50);
      circle2   = new Circle(100, 100, 50);
      context   = document.querySelector("canvas").getContext("2d");
      length    = document.getElementById("length");
      sum_radii = document.getElementById("sum_radii");

      window.addEventListener("resize",             update);

      /* We add all the input events to the canvas and use the same event handler.
      passive:false is used to indicate that this input will override the normal
      mouse and touch event actions that the browser might recognize as a scroll
      or zoom command. */
      context.canvas.addEventListener("mousedown",  handleInput, { passive:false });
      context.canvas.addEventListener("mousemove",  handleInput, { passive:false });
      context.canvas.addEventListener("mouseup",    handleInput, { passive:false });
      context.canvas.addEventListener("touchend",   handleInput, { passive:false });
      context.canvas.addEventListener("touchmove",  handleInput, { passive:false });
      context.canvas.addEventListener("touchstart", handleInput, { passive:false });

      document.querySelector("a").addEventListener("click", changeRadii);

      update();
      update();

      /* We call update twice because the output can't be calculated until the canvas is
      resized, but since the output changing size can cause the canvas to resize we
      have to call it twice. */

    </script>

  </body>

</html>


================================================
FILE: content/circle-collision-response/circle-collision-response.html
================================================
<!DOCTYPE html>

<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision
response. -->

<html>

  <head>

    <meta name = "viewport"    content = "width=device-width">
    <meta name = "description" content = "Learn circle collision response from pure JavaScript source code!">

    <title>Circle Collision Response</title>

    <style>

      * { margin:0; padding:0; box-sizing:border-box; }

      html { height:100%; width:100%; }

      body {

        background-color:#202830;
        display:grid;
        grid-row-gap:16px;
        grid-template-columns:100%;
        grid-template-rows:min-content auto;
        height:100%;
        justify-items:center;
        padding:16px;
        width:100%;

      }

      p { color:#ffffff; font-size:1.25em; max-width:680px; }

      a { color:#f08000; cursor:pointer; display:inline-block; font-weight:600; user-select:none; }

      canvas { height:100%; max-width:680px; width:100%; }

    </style>

  </head>

  <body>

    <p>To resolve collision between two circles, one of the circles must be "pushed"
    out of the other until the distance between their center points is greater than
    the sum of their radii. The circle should be moved out of collision along the
    vector between the center points of the two circles. Its length is <span id = "length" style = "display:inline-block;">0</span>.
    the sum of the radii is <span id = "sum_radii" style = "display:inline-block;">0</span>. <a>change radii</a></p>

    <canvas></canvas>

    <script type = "text/javascript">

    // Frank Poth 04/16/2018

      /* The Circle class defines a simple circle with its center point at x, y. */
      const Circle = function(x, y, radius) {

        this.x      = x;
        this.y      = y;
        this.radius = radius;

      };

      /* Fires when you click the "change radii" <a> tag. */
      function changeRadii(event) {

        circle1.radius = Math.floor(Math.random() * 41 + 10);
        circle2.radius = Math.floor(Math.random() * 41 + 10);

        update();

      };

      /* To test if two circles are overlapping, you simply test to see if the distance
      between their two center points is less than their combined radii. The formula
      for distance between two points is sqrt((p1.x - p2.x)^2 + (p1.y - p2.y)^2), but
      since sqrt is an expensive operation, we can simply stop after we square the two
      differences and test against the square of the sum of the radii. */
      function collideCircle(circle1, circle2) {

        /* first we get the x and y distance between the two circles. */
        let distance_x = circle1.x      - circle2.x;
        let distance_y = circle1.y      - circle2.y;
        /* Then we get the sum of their radii. */
        let radii_sum  = circle1.radius + circle2.radius;

        /* Then we test to see if the square of their distance is greater than the
        square of their radii. If it is, then there is no collision. If it isn't,
        then we have a collision. */
        if (distance_x * distance_x + distance_y * distance_y <= radii_sum * radii_sum) return true;

        return false;

      }

      function resolveCircle(c1, c2) {

        let distance_x = c1.x      - c2.x;
        let distance_y = c1.y      - c2.y;
        let radii_sum  = c1.radius + c2.radius;
        let length = Math.sqrt(distance_x * distance_x + distance_y * distance_y) || 1;
        let unit_x = distance_x / length;
        let unit_y = distance_y / length;

        c1.x = c2.x + (radii_sum + 1) * unit_x;
        c1.y = c2.y + (radii_sum + 1) * unit_y;

      }

      /* Fills the specified circle with the specified color. */
      function fillCircle(circle, color) {

        context.fillStyle = color;
        context.beginPath();
        //arc(centerX, centerY, radius, start_radian, stop_radian);
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
        context.fill();

      }

      /* Draws the boundary of the specified circle with the specified color. */
      function strokeCircle(circle, color) {

        context.strokeStyle = color;
        context.beginPath();
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
        context.stroke();

      }

      /* Called whenever user input is entered via mouse or touch. */
      function handleInput(event) {

        /* Calling event.preventDefault is important so we don't scroll or scale
        when we interact with the canvas. We can call it because the event listener's
        passive property is set to false. */
        event.preventDefault();

        /* We need to know where the canvas is located in the browser window to
        get the offset position to subtract from the input location. */
        let canvas_rectangle = context.canvas.getBoundingClientRect();
        let x = undefined;
        let y = undefined;

        switch(event.type) {

          /* For any touch event, we must get the client position from the targetTouches array. */
          case "touchend": case "touchmove": case "touchstart":

          x = event.targetTouches[0].clientX;
          y = event.targetTouches[0].clientY;

          break;
          /* The default would be mouse events, and these just use clientX and clientY. */
          default:

          x = event.clientX;
          y = event.clientY;

        }

        /* Set circle1's position equal to the input position offset by the bounding rect of the canvas. */
        circle1.x = x - canvas_rectangle.left;
        circle1.y = y - canvas_rectangle.top;

        update();// draw everything to the screen

      }

      /* Here we draw everything and we also test for collision with the collideCircle method. */
      function update() {

        /* Keep circle2 in the center of the canvas. */
        circle2.x = context.canvas.clientWidth  * 0.5;
        circle2.y = context.canvas.clientHeight * 0.5;

        let collision = false;

        if (collideCircle(circle1, circle2)) {// collision!

          resolveCircle(circle1, circle2);// Response!

          collision = true;

        }

        /* Set the output values. */
        length.innerHTML = Math.round(Math.sqrt(Math.pow(circle1.x - circle2.x, 2) + Math.pow(circle1.y - circle2.y, 2)));
        length.style.width = length.clientWidth + "px";
        sum_radii.innerHTML = circle1.radius + circle2.radius;
        sum_radii.style.width = sum_radii.clientWidth + "px";

        /* We have to reset the canvas height and width in case the p element
        gets resized. If it does the css will adjust the canvas height and skew
        the image inside. To combat this, we just get the computed width and height
        on each update. */
        context.canvas.height = context.canvas.clientHeight;
        context.canvas.width = context.canvas.clientWidth;

        /* Clear the canvas to gray. */
        context.fillStyle = "#283038";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        if (collision) {

          strokeCircle(circle1, "#ffffff");
          strokeCircle(circle2, "#ffffff");

        } else {

          fillCircle(circle1, "#0080f0");
          fillCircle(circle2, "#f08000");

        }

        /* Draw the white line. */
        context.strokeStyle = "#ffffff";
        context.beginPath();
        context.moveTo(circle1.x, circle1.y);
        context.lineTo(circle2.x, circle2.y);
        context.stroke();

      };

      var circle1, circle2, context, length, sum_radii;

      circle1   = new Circle(100, 100, 50);
      circle2   = new Circle(100, 100, 50);
      context   = document.querySelector("canvas").getContext("2d");
      length    = document.getElementById("length");
      sum_radii = document.getElementById("sum_radii");

      /* We add all the input events to the canvas and use the same event handler.
      passive:false is used to indicate that this input will override the normal
      mouse and touch event actions that the browser might recognize as a scroll
      or zoom command. */
      context.canvas.addEventListener("mousedown",  handleInput, { passive:false });
      context.canvas.addEventListener("mousemove",  handleInput, { passive:false });
      context.canvas.addEventListener("mouseup",    handleInput, { passive:false });
      context.canvas.addEventListener("touchend",   handleInput, { passive:false });
      context.canvas.addEventListener("touchmove",  handleInput, { passive:false });
      context.canvas.addEventListener("touchstart", handleInput, { passive:false });

      window.addEventListener("resize", update);

      document.querySelector("a").addEventListener("click", changeRadii);

      update();
      update();// The second call is used to position the yellow circle the right way.

    </script>

  </body>

</html>


================================================
FILE: content/collision/collision.css
================================================
/* Frank Poth 08/29/2017 */

* {

  box-sizing: border-box;
  margin:0;
  padding:0;

}

html, body {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  justify-items:center;

}

h1 {

  word-wrap:break-word;

}


================================================
FILE: content/collision/collision.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <title>Collision</title>
    <meta name = "viewport" content = "width=device-width">
    <link href = "collision.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Square Collision</h1>

    <canvas></canvas>

    <script src = "collision.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/collision/collision.js
================================================
// Frank Poth 08/29/2017

var context, controller, Rectangle, red, white, loop, resize;

context = document.querySelector("canvas").getContext("2d");

controller = {

  // mouse or finger position
  pointer_x:0,
  pointer_y:0,

  move:function(event) {

    // This will give us the location of our canvas element
    var rectangle = context.canvas.getBoundingClientRect();

    // store the position of the move event inside the pointer variables
    controller.pointer_x = event.clientX - rectangle.left;
    controller.pointer_y = event.clientY - rectangle.top;

  }

};

Rectangle = function(x, y, width, height, color) {

  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;

  this.color = color;

};

Rectangle.prototype = {

  draw:function() {// draws rectangle to canvas

    context.beginPath();
    context.rect(this.x, this.y, this.width, this.height);
    context.fillStyle = this.color;
    context.fill();

  },

  // get the four side coordinates of the rectangle
  get bottom() { return this.y + this.height; },
  get left() { return this.x; },
  get right() { return this.x + this.width; },
  get top() { return this.y; },

  testCollision:function(rectangle) {

    if (this.top > rectangle.bottom || this.right < rectangle.left || this.bottom < rectangle.top || this.left > rectangle.right) {

      return false;

    }

    return true;

  }

};

red = new Rectangle(0, 0, 64, 64, "#ff0000");
white = new Rectangle(context.canvas.width * 0.5 - 32, context.canvas.height * 0.5 - 32, 64, 64, "#ffffff");

loop = function(time_stamp) {

  red.x = controller.pointer_x - 32;
  red.y = controller.pointer_y - 32;

  context.fillStyle = "#303840";
  context.fillRect(0, 0, context.canvas.width, context.canvas.height);

  white.draw();
  red.draw();

  if (red.testCollision(white)) {

    context.beginPath();
    context.rect(red.x, red.y, red.width, red.height);
    context.rect(white.x, white.y, white.width, white.height);
    context.strokeStyle = "#ffffff";
    context.stroke();

  }

  window.requestAnimationFrame(loop);

};

// just keeps the canvas element sized appropriately
resize = function(event) {

  context.canvas.width = document.documentElement.clientWidth - 32;

  if (context.canvas.width > document.documentElement.clientHeight) {

    context.canvas.width = document.documentElement.clientHeight;

  }

  context.canvas.height = Math.floor(context.canvas.width * 0.5625);

  white.x = context.canvas.width * 0.5 - 32;
  white.y = context.canvas.height * 0.5 - 32;

};

context.canvas.addEventListener("mousemove", controller.move);
context.canvas.addEventListener("touchmove", controller.move, {passive:true});

window.addEventListener("resize", resize, {passive:true});

resize();

// start the game loop
window.requestAnimationFrame(loop);


================================================
FILE: content/control/control.css
================================================
/* Frank Poth 08/13/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

html, body {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  justify-content:center;

}

canvas {

  background-color:#ffffff;

}


================================================
FILE: content/control/control.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <title>PoP Vlog - Control</title>
    <link href = "control.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Control</h1>

    <canvas></canvas>

    <p>Use the keyboard to move the red square.</p>

    <script src = "control.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/control/control.js
================================================
// Frank Poth 08/13/2017

var context, controller, rectangle, loop;

context = document.querySelector("canvas").getContext("2d");

context.canvas.height = 180;
context.canvas.width = 320;

rectangle = {

  height:32,
  jumping:true,
  width:32,
  x:144, // center of the canvas
  x_velocity:0,
  y:0,
  y_velocity:0

};

controller = {

  left:false,
  right:false,
  up:false,
  keyListener:function(event) {

    var key_state = (event.type == "keydown")?true:false;

    switch(event.keyCode) {

      case 37:// left key
        controller.left = key_state;
      break;
      case 38:// up key
        controller.up = key_state;
      break;
      case 39:// right key
        controller.right = key_state;
      break;

    }

  }

};

loop = function() {

  if (controller.up && rectangle.jumping == false) {

    rectangle.y_velocity -= 20;
    rectangle.jumping = true;

  }

  if (controller.left) {

    rectangle.x_velocity -= 0.5;

  }

  if (controller.right) {

    rectangle.x_velocity += 0.5;

  }

  rectangle.y_velocity += 1.5;// gravity
  rectangle.x += rectangle.x_velocity;
  rectangle.y += rectangle.y_velocity;
  rectangle.x_velocity *= 0.9;// friction
  rectangle.y_velocity *= 0.9;// friction

  // if rectangle is falling below floor line
  if (rectangle.y > 180 - 16 - 32) {

    rectangle.jumping = false;
    rectangle.y = 180 - 16 - 32;
    rectangle.y_velocity = 0;

  }

  // if rectangle is going off the left of the screen
  if (rectangle.x < -32) {

    rectangle.x = 320;

  } else if (rectangle.x > 320) {// if rectangle goes past right boundary

    rectangle.x = -32;

  }

  context.fillStyle = "#202020";
  context.fillRect(0, 0, 320, 180);// x, y, width, height
  context.fillStyle = "#ff0000";// hex for red
  context.beginPath();
  context.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
  context.fill();
  context.strokeStyle = "#202830";
  context.lineWidth = 4;
  context.beginPath();
  context.moveTo(0, 164);
  context.lineTo(320, 164);
  context.stroke();

  // call update when the browser is ready to draw again
  window.requestAnimationFrame(loop);

};

window.addEventListener("keydown", controller.keyListener)
window.addEventListener("keyup", controller.keyListener);
window.requestAnimationFrame(loop);


================================================
FILE: content/cube/cube.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta charset = "utf-8">
    <meta name = "viewport" content = "width=device-width">
    <title>Cube</title>

    <style>

      * { margin:0; padding:0; }

      html, body { height:100%; width:100%; }

      canvas { height:100%; position:fixed; width:100%; }

    </style>

  </head>

  <body>

    <canvas></canvas>

    <script type = "text/javascript">

      const Point2D = function(x, y) { this.x = x; this.y = y; };

      const Point3D = function(x, y, z) { this.x = x; this.y = y; this.z = z; };

      const Cube = function(x, y, z, size) {

        Point3D.call(this, x, y, z);

        size *= 0.5;

        this.vertices = [new Point3D(x - size, y - size, z - size),
                         new Point3D(x + size, y - size, z - size),
                         new Point3D(x + size, y + size, z - size),
                         new Point3D(x - size, y + size, z - size),
                         new Point3D(x - size, y - size, z + size),
                         new Point3D(x + size, y - size, z + size),
                         new Point3D(x + size, y + size, z + size),
                         new Point3D(x - size, y + size, z + size)];

        this.faces = [[0, 1, 2, 3], [0, 4, 5, 1], [1, 5, 6, 2], [3, 2, 6, 7], [0, 3, 7, 4], [4, 7, 6, 5]];

      };

      Cube.prototype = {

        rotateX:function(radian) {

          var cosine = Math.cos(radian);
          var sine   = Math.sin(radian);

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let p = this.vertices[index];

            let y = (p.y - this.y) * cosine - (p.z - this.z) * sine;
            let z = (p.y - this.y) * sine + (p.z - this.z) * cosine;

            p.y = y + this.y;
            p.z = z + this.z;

          }

        },

        rotateY:function(radian) {

          var cosine = Math.cos(radian);
          var sine   = Math.sin(radian);

          for (let index = this.vertices.length - 1; index > -1; -- index) {

            let p = this.vertices[index];

            let x = (p.z - this.z) * sine + (p.x - this.x) * cosine;
            let z = (p.z - this.z) * cosine - (p.x - this.x) * sine;

            p.x = x + this.x;
            p.z = z + this.z;

          }

        }

      };

      var context = document.querySelector("canvas").getContext("2d");
      var pointer = new Point2D(0, 0);
      var cube = new Cube(0, 0, 400, 200);

      var height = document.documentElement.clientHeight;
      var width = document.documentElement.clientWidth;

      function project(points3d, width, height) {

        var points2d = new Array(points3d.length);

        var focal_length = 200;

        for (let index = points3d.length - 1; index > -1; -- index) {

          let p = points3d[index];

          let x = p.x * (focal_length / p.z) + width * 0.5;
          let y = p.y * (focal_length / p.z) + height * 0.5;

          points2d[index] = new Point2D(x, y);

        }

        return points2d;

      }

      function loop() {

        window.requestAnimationFrame(loop);

        height = document.documentElement.clientHeight;
        width = document.documentElement.clientWidth;

        context.canvas.height = height;
        context.canvas.width  = width;

        context.fillStyle = "#ffffff";
        context.fillRect(0, 0, width, height);

        context.strokeStyle = "#ffffff";

        cube.rotateX(pointer.y * 0.0001);
        cube.rotateY(-pointer.x * 0.0001);

        context.fillStyle = "#0080f0";

        var vertices = project(cube.vertices, width, height);

        for (let index = cube.faces.length - 1; index > -1; -- index) {

          let face = cube.faces[index];

          let p1 = cube.vertices[face[0]];
          let p2 = cube.vertices[face[1]];
          let p3 = cube.vertices[face[2]];

          let v1 = new Point3D(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);
          let v2 = new Point3D(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z);

          let n  = new Point3D(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);

          if (-p1.x * n.x + -p1.y * n.y + -p1.z * n.z <= 0) {

            context.beginPath();
            context.moveTo(vertices[face[0]].x, vertices[face[0]].y);
            context.lineTo(vertices[face[1]].x, vertices[face[1]].y);
            context.lineTo(vertices[face[2]].x, vertices[face[2]].y);
            context.lineTo(vertices[face[3]].x, vertices[face[3]].y);
            context.closePath();
            context.fill();
            context.stroke();

          }


        }

      }

      loop();

      window.addEventListener("click", (event) => {

        pointer.x = event.pageX - width * 0.5;
        pointer.y = event.pageY - height * 0.5;

      });

    </script>

  </body>

</html>


================================================
FILE: content/dino/dino.css
================================================
/* Frank Poth 12/24/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;
  user-select:none;

}

html {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  grid-template-columns:auto;
  grid-template-rows:auto auto auto;
  height:100%;
  justify-items:center;
  padding:0 8px;
  width:100%;

}


================================================
FILE: content/dino/dino.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <meta name = "description" content = "A simple game in which a dinosaur jumps over stuff based on Chrome's endless runner.">

    <link href = "dino.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - Dino Run</h1>

    <canvas></canvas>

    <p>Click or tap to jump!</p>

    <script src = "dino.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/dino/dino.js
================================================
// Frank Poth 12/24/2017

/* This example has a lot packed into it. It has a scrolling tile based background.
The rightmost column is randomly generated when scrolling. There is animation.
There is collision detection between all moving objects and the world as well as
the player and the meteors and tarpits. There is an effect that turns the screen
red when a meteor spawns using image data. I implement object pooling to avoid using
"new" to create new objects. Some of this stuff I've covered in old tutorials, and
some stuff I have not covered. */

(function() { "use strict";

  const TILE_SIZE = 16;
  const WORLD_HEIGHT = 144;
  const WORLD_WIDTH = 256;

  //// CLASSES ////

  var Animation = function(frame_set, delay) {

    this.count = 0;// Counts the number of game cycles since the last frame change.
    this.delay = delay;// The number of game cycles to wait until the next frame change.
    this.frame_value = frame_set[0];// The value in the sprite sheet of the sprite image / tile to display.
    this.frame_index = 0;// The frame's index in the current animation frame set.
    this.frame_set = frame_set;// The current animation frame set that holds sprite tile values.

  };

  Animation.prototype = {

    /* This changes the current animation frame set. For example, if the current
    set is [0, 1], and the new set is [2, 3], it changes the set to [2, 3]. It also
    sets the delay. */
    change:function(frame_set, delay = 15) {

      if (this.frame_set != frame_set) {// If the frame set is different:

        this.count = 0;// Reset the count.
        this.delay = delay;// Set the delay.
        this.frame_index = 0;// Start at the first frame in the new frame set.
        this.frame_set = frame_set;// Set the new frame set.
        this.frame_value = this.frame_set[this.frame_index];// Set the new frame value.

      }

    },

    /* Call this on each game cycle. */
    update:function() {

      this.count ++;// Keep track of how many cycles have passed since the last frame change.

      if (this.count >= this.delay) {// If enough cycles have passed, we change the frame.

        this.count = 0;// Reset the count.
        /* If the frame index is on the last value in the frame set, reset to 0.
        If the frame index is not on the last value, just add 1 to it. */
        this.frame_index = (this.frame_index == this.frame_set.length - 1) ? 0 : this.frame_index + 1;
        this.frame_value = this.frame_set[this.frame_index];// Change the current frame value.

      }

    }

  };

  /* A frame just keeps track of a physical position inside the tile sheet for blitting. */
  var Frame = function(x, y, width, height) {

    this.height = height;
    this.width  = width;
    this.x      = x;
    this.y      = y;

  };

  /* A Pool object manages objects. The objects array holds all objects that are
  currently in use, and the pool holds objects that are not in use. By storing objects
  that would otherwise be deleted, we can reuse them instead of creating totally new
  instances with the new operator. Recycling saves memory. Do it. */
  var Pool = function(object) {

    this.object = object;// The constructor of the object we are pooling.
    this.objects = [];// The array of objects in use.
    this.pool = [];// The array of objects not in use.

  };

  Pool.prototype = {

    /* Get an object from the pool or create a new object. Pool expects objects to
    have a few basic functions, like reset. */
    get:function(parameters) {

      if (this.pool.length != 0) {

        let object = this.pool.pop();
        object.reset(parameters);
        this.objects.push(object);

      } else {

        this.objects.push(new this.object(parameters.x, parameters.y));

      }

    },

    store:function(object) {

      let index = this.objects.indexOf(object);

      if (index != -1) {

        this.pool.push(this.objects.splice(index, 1)[0]);

      }

    },

    storeAll:function() {

      for (let index = this.objects.length - 1; index > -1; -- index) {

        this.pool.push(this.objects.pop());

      }

    }

  };

  var Meteor = function(x, y) {

    this.alive = true;// Meteor dies when it goes offscreen.
    this.animation = new Animation(display.tile_sheet.frame_sets[1], 8);
    this.grounded = false;
    this.smoke = false;// smoke values are used for spawning smoke particles.
    this.smoke_count = 0;
    this.smoke_delay = Math.floor(Math.random() * 10 + 5);
    this.height = Math.floor(Math.random() * 16 + 24); this.width = this.height;
    this.x = x; this.y = y - this.height * 0.5;
    let direction = Math.PI * 1.75 + Math.random() * Math.PI * 0.1;// The trajectory.
    this.x_velocity = Math.cos(direction) * 3; this.y_velocity = -Math.sin(direction) * 3;

  };

  /* All game objects are expected to have collideWorld and CollideObject functions,
  as well as update and reset functions. If this were a strongly typed language, I
  would be using a base class called GameObject or something. */
  Meteor.prototype = {

    constructor:Meteor,

    collideObject:function(player) {

      let vector_x = player.x + player.width * 0.5 - this.x - this.width * 0.5;
      let vector_y = player.y + player.height * 0.5 - this.y - this.height * 0.5;
      let combined_radius = player.height * 0.5 + this.width * 0.5;

      if (vector_x * vector_x + vector_y * vector_y < combined_radius * combined_radius) {

        player.alive = false;
        player.animation.change(display.tile_sheet.frame_sets[5], 10);

      }

    },

    collideWorld:function() {

      if (this.x + this.width < 0) {

        this.alive = false;
        return;

      }

      if (this.y + this.height > WORLD_HEIGHT - 6) {

        this.x_velocity = -game.speed;
        this.grounded = true;
        this.y = WORLD_HEIGHT - this.height - 6;

      }

    },

    reset:function(parameters) {

      this.alive = true;
      this.animation.change(display.tile_sheet.frame_sets[1], 8);
      this.grounded = false;
      this.x = parameters.x;
      let direction = Math.PI * 1.75 + Math.random() * Math.PI * 0.1;
      this.x_velocity = Math.cos(direction) * 3;
      this.y = parameters.y;
      this.y_velocity = -Math.sin(direction) * 3;

    },

    update:function() {

      if (!this.grounded) {

        this.animation.update();
        this.y += this.y_velocity;

      } else {

        this.x_velocity = -game.speed;

      }

      this.x += this.x_velocity;

      this.smoke_count ++;
      if (this.smoke_count == this.smoke_delay) {

        this.smoke_count = 0;
        this.smoke = true;

      }

    }

  };

  var Smoke = function(x, y, x_velocity, y_velocity) {

    this.alive = true;
    this.animation = new Animation(display.tile_sheet.frame_sets[2], 8);
    this.life_count = 0;
    this.life_time = Math.random() * 20 + 30;
    this.height = 8 + Math.floor(Math.random() * 8); this.width = this.height;
    this.x = x; this.y = y;
    this.x_velocity = x_velocity; this.y_velocity = y_velocity;

  };

  Smoke.prototype = {

    constructor:Smoke,

    collideWorld:function() {

      if (this.x > WORLD_WIDTH || this.y > WORLD_HEIGHT - 20) {

        this.alive = false;

      }

    },

    reset:function(parameters) {

      this.alive = true;
      this.life_count = 0;
      this.life_time = Math.random() * 20 + 30;
      this.x          = parameters.x;
      this.x_velocity = parameters.x_velocity;
      this.y          = parameters.y;
      this.y_velocity = parameters.y_velocity;

    },

    update:function() {

      this.animation.update();
      this.x += this.x_velocity;
      this.y += this.y_velocity;

      this.life_count ++;

      if (this.life_count > this.life_time) {

        this.alive = false;

      }

    }

  };

  var TarPit = function(x, y) {

    this.alive = true;
    this.animation = new Animation(display.tile_sheet.frame_sets[0], 8);
    this.height = 30; this.width = Math.floor(Math.random() * 64 + 48);
    this.x = x; this.y = y;

  };

  TarPit.prototype = {

    constructor:TarPit,

    collideObject:function(player) {

    },

    collideObject:function(object) {

      if (!object.jumping && object.x + object.width * 0.5 > this.x + this.width * 0.2 && object.x + object.width * 0.5 < this.x + this.width * 0.8) {

        object.alive = false;
        object.animation.change(display.tile_sheet.frame_sets[4], 10);

      }

    },

    collideWorld:function() {

      if (this.x + this.width < 0) this.alive = false;

    },

    reset:function(parameters) {

      this.alive = true;
      this.width = Math.floor(Math.random() * 64 + 48);
      this.x = parameters.x;
      this.y = parameters.y;

    },

    update:function(){

      this.animation.update();
      this.x -= game.speed;

    }

  };

  var controller, display, game;

  /* This is awesome. I can use the same event handler for all mouseup, mousedown,
  touchstart, and touchend events. This controller works on everything! */
  controller = {

    active:false, state:false,

    onOff:function(event) {

      event.preventDefault();

      let key_state = (event.type == "mousedown" || event.type == "touchstart") ? true : false;

      if (controller.state != key_state) controller.active = key_state;
      controller.state  = key_state;

    }

  };

  display = {

    buffer:document.createElement("canvas").getContext("2d"),
    context:document.querySelector("canvas").getContext("2d"),

    tint:0,// The red tint value to add to the buffer's red channel when a meteor is on screen.

    tile_sheet: {

      columns:undefined,// Set up in INITIALIZE section.
      frames: [new Frame( 0, 32, 24, 16), new Frame(24, 32, 24, 16),// tar pit
               new Frame(64, 32, 16, 16), new Frame(80, 32, 16, 16),// Meteor
               new Frame(96, 32,  8,  8), new Frame(104,32,  8,  8), new Frame(96, 40,  8,  8), new Frame(104,40,  8, 8),// smoke
               new Frame( 0, 48, 28, 16), new Frame(28, 48, 28, 16), new Frame(56, 48, 28, 16), new Frame(84, 48, 28, 16), new Frame( 0, 64, 28, 16), new Frame(28, 64, 28, 16), new Frame(56, 64, 28, 16), new Frame(84, 64, 28, 16),//dino run
               new Frame( 0, 80, 28, 16), new Frame(28, 80, 28, 16), new Frame(56, 80, 28, 16), new Frame(84, 80, 28, 16), new Frame( 0, 96, 28, 16), new Frame(28, 96, 28, 16),//dino sink
               new Frame(56, 96, 28, 16), new Frame(84, 96, 28, 16), new Frame( 0,112, 28, 16), new Frame(28,112, 28, 16), new Frame(56,112, 28, 16), new Frame(84,112, 28, 16)//dino crisp
              ],

      frame_sets:[[ 0, 1],//tar pit
                  [ 2, 3],//Meteor
                  [ 4, 5, 6, 7],//smoke
                  [ 8, 9,10,11,12,13,14,15],//dino run
                  [16,17,18,19,20,21],//dino sink
                  [22,23,24,25,26,27]//dino crisp

      ],
      image:new Image()// The tile sheet image is loaded at the bottom of this file.

    },

    render:function() {

      // Draw Tiles
      for (let index = game.area.map.length - 1; index > -1; -- index) {

        let value = game.area.map[index];

        this.buffer.drawImage(this.tile_sheet.image, (value % this.tile_sheet.columns) * TILE_SIZE, Math.floor(value / this.tile_sheet.columns) * TILE_SIZE, TILE_SIZE, TILE_SIZE, (index % game.area.columns) * TILE_SIZE - game.area.offset, Math.floor(index / game.area.columns) * TILE_SIZE, TILE_SIZE, TILE_SIZE);

      }

      // Draw distance
      this.buffer.font = "20px Arial";
      this.buffer.fillStyle = "#ffffff";
      this.buffer.fillText(String(Math.floor(game.distance/10) + " / " + Math.floor(game.max_distance/10)), 10, 20);

      // Draw TarPits
      for (let index = game.object_manager.tarpit_pool.objects.length - 1; index > -1; -- index) {

        let tarpit = game.object_manager.tarpit_pool.objects[index];

        let frame = this.tile_sheet.frames[tarpit.animation.frame_value];

        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, tarpit.x, tarpit.y, tarpit.width, tarpit.height);

      }

      // Draw Player
      let frame = this.tile_sheet.frames[game.player.animation.frame_value];

      this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, game.player.x, game.player.y, game.player.width, game.player.height);

      // Draw Meteors
      for (let index = game.object_manager.meteor_pool.objects.length - 1; index > -1; -- index) {

        let meteor = game.object_manager.meteor_pool.objects[index];

        let frame = this.tile_sheet.frames[meteor.animation.frame_value];

        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, meteor.x, meteor.y, meteor.width, meteor.height);

      }

      // Draw Smoke
      for (let index = game.object_manager.smoke_pool.objects.length - 1; index > -1; -- index) {

        let smoke = game.object_manager.smoke_pool.objects[index];

        let frame = this.tile_sheet.frames[smoke.animation.frame_value];

        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, smoke.x, smoke.y, smoke.width, smoke.height);

      }

      // Draw tint if a meteor is on screen
      if (game.object_manager.meteor_pool.objects.length != 0) {

        this.tint = (this.tint < 80) ? this.tint + 1 : 80;

      } else {// Reduce tint otherwise

        this.tint = (this.tint > 0) ? this.tint - 2 : 0;

      }

      if (this.tint != 0) {// If there is a tint to draw, apply it to the buffer

        let image_data = this.buffer.getImageData(0, 0, WORLD_WIDTH, WORLD_HEIGHT);
        let data = image_data.data;

        for (let index = data.length - 4; index > -1; index -= 4) {

          data[index] += this.tint;

        }

        this.buffer.putImageData(image_data, 0, 0);

      }

      this.context.drawImage(this.buffer.canvas, 0, 0, WORLD_WIDTH, WORLD_HEIGHT, 0, 0, this.context.canvas.width, this.context.canvas.height);

    },

    resize:function(event) {

      display.context.canvas.width = document.documentElement.clientWidth - 16;

      if (display.context.canvas.width > document.documentElement.clientHeight - 16) {

        display.context.canvas.width = document.documentElement.clientHeight - 16;

      }

      display.context.canvas.height = display.context.canvas.width * 0.5625;

      display.buffer.imageSmoothingEnabled = false;
      display.context.imageSmoothingEnabled = false;

      display.render();

    }

  };

  game = {

    distance:0,
    max_distance:0,
    speed:3,

    area: {

      columns:17,
      offset:0,
      map:[ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0,
            0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1,
            1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,
            1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
            0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
            1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0,
            1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0,
            2, 2, 2, 3, 2, 2, 3, 2, 4, 6, 7, 7, 6, 9, 2, 3, 2,
           10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10],

      /* Takes care of scrolling the background and generating the next column to
      display on the far right of the map. */
      scroll:function() {

        game.distance += game.speed;

        if (game.distance > game.max_distance) game.max_distance = game.distance;

        this.offset += game.speed;
        if (this.offset >= TILE_SIZE) {

          this.offset -= TILE_SIZE;

          /* This loop removes the first column and inserts a randomly generated
          last column for the top 7 rows. This handles random sky generation. */
          for (let index = this.map.length - this.columns * 3 ; index > -1; index -= this.columns) {

            this.map.splice(index, 1);
            this.map.splice(index + this.columns - 1, 0, Math.floor(Math.random() * 2));

          }

          /* This next part replaces the grass with an appropriate grass tile. I
          made it a bit more complex than it needed to be, but the tiles actually
          reconcile their edges with the tile directly to the left. */
          this.map.splice(this.columns * 7, 1);

          let right_index = this.columns * 8 - 1;
          let value = this.map[right_index - 1];

          switch(value) {

            case 2: case 3: value = [2, 3, 2, 3, 2, 3, 2, 3, 4, 5][Math.floor(Math.random() * 10)]; break;
            case 4: case 5: value = [6, 7][Math.floor(Math.random() * 2)]; break;
            case 6: case 7: value = [6, 7, 8, 9][Math.floor(Math.random() * 4)]; break;
            case 8: case 9: value = [2, 3][Math.floor(Math.random() * 2)]; break;

          }

          this.map.splice(right_index, 0, value);

          // The last row stays the same. It's just dirt.

        }

      }

    },

    engine: {

      /* Fixed time step game loop!! */
      afrequest:undefined,// animation frame request reference
      accumulated_time:window.performance.now(),
      time_step:1000/60,// update rate

      loop:function(time_stamp) {

        /* How easy does this look? This is a fixed step loop with frame dropping.
        Amazingly it's super simple and only a few lines. This will make your game
        run at the same speed on all devices. Now that I look at it, I think there
        may be a better way to implement this because entire frames can be dropped
        without updating or rendering. Rather than fixing this now, I will just leave it.
        Ideally, I would utilize the free time and not do both updates and renderings
        at the same time unless I have to... Another day... This does work fine, though. */
        if (time_stamp >= game.engine.accumulated_time + game.engine.time_step) {

          if (time_stamp - game.engine.accumulated_time >= game.engine.time_step * 4) {

            game.engine.accumulated_time = time_stamp;

          }

          while(game.engine.accumulated_time < time_stamp) {

            game.engine.accumulated_time += game.engine.time_step;

            game.engine.update();

          }

          display.render();

        }

        window.requestAnimationFrame(game.engine.loop);

      },

      start:function() {// Start the game loop.

        this.afrequest = window.requestAnimationFrame(this.loop);

      },

      update:function() {// Update the game logic.

        /* Slowly increase speed and cap it when it gets too high. */
        game.speed = (game.speed >= TILE_SIZE * 0.5)? TILE_SIZE * 0.5 : game.speed + 0.001;
        /* Make sure the player's animation delay is keeping up with the scroll speed. */
        game.player.animation.delay = Math.floor(10 - game.speed);
        game.area.scroll();// Scroll!!!

        if (game.player.alive) {

          if (controller.active && !game.player.jumping) {// Get user input

            controller.active = false;
            game.player.jumping = true;
            game.player.y_velocity -= 15;
            game.player.animation.change([10], 15);

          }

          if (game.player.jumping == false) {

            game.player.animation.change(display.tile_sheet.frame_sets[3], Math.floor(TILE_SIZE - game.speed));

          }

          game.player.update();

          if (game.player.y > TILE_SIZE * 6 - TILE_SIZE * 0.25) {// Collide with floor

            controller.active = false;
            game.player.y = TILE_SIZE * 6 - TILE_SIZE * 0.25;
            game.player.y_velocity = 0;
            game.player.jumping = false;

          }

        } else {

          game.player.x -= game.speed;
          game.speed *= 0.9;

          if (game.player.animation.frame_index == game.player.animation.frame_set.length - 1) game.reset();

        }

        game.player.animation.update();

        game.object_manager.spawn();
        game.object_manager.update();

      }

    },

    /* Manages all non player objects. */
    object_manager: {

      count:0,
      delay:100,

      meteor_pool:new Pool(Meteor),
      smoke_pool:new Pool(Smoke),
      tarpit_pool:new Pool(TarPit),

      spawn:function() {

        this.count ++;

        if (this.count == this.delay) {

          this.count = 0;
          this.delay = 100;// + Math.floor(Math.random() * 200 - 10 * game.speed);

          /* Pick randomly between tarpits and meteors */
          if (Math.random() > 0.5) {

            this.tarpit_pool.get( {x: WORLD_WIDTH, y:WORLD_HEIGHT - 30} );

          } else {

            this.meteor_pool.get( {x: WORLD_WIDTH * 0.2, y: -32 } );

          }

        }

      },

      update:function() {

        for (let index = this.meteor_pool.objects.length - 1; index > -1; -- index) {

          let meteor = this.meteor_pool.objects[index];

          meteor.update();

          meteor.collideObject(game.player);

          meteor.collideWorld();

          if (meteor.smoke) {

            meteor.smoke = false;

            let parameters = { x:meteor.x + Math.random() * meteor.width, y:undefined, x_velocity:undefined, y_velocity:undefined };

            if (meteor.grounded) {

              parameters.y = meteor.y + Math.random() * meteor.height * 0.5;
              parameters.x_velocity = Math.random() * 2 - 1 - game.speed;
              parameters.y_velocity = Math.random() * -1;

            } else {

              parameters.y = meteor.y + Math.random() * meteor.height;
              parameters.x_velocity = meteor.x_velocity * Math.random();
              parameters.y_velocity = meteor.y_velocity * Math.random();

            }

            this.smoke_pool.get(parameters);

          }

          if (!meteor.alive) {

            this.meteor_pool.store(meteor);

          };

        }

        for (let index = this.smoke_pool.objects.length - 1; index > -1; -- index) {

          let smoke = this.smoke_pool.objects[index];

          smoke.update();

          smoke.collideWorld();

          if (!smoke.alive) this.smoke_pool.store(smoke);

        }

        for (let index = this.tarpit_pool.objects.length - 1; index > -1; -- index) {

          let tarpit = this.tarpit_pool.objects[index];

          tarpit.update();

          tarpit.collideObject(game.player);

          tarpit.collideWorld();

          if (!tarpit.alive) this.tarpit_pool.store(tarpit);

        }

      }

    },

    player: {

      alive:true,
      animation:new Animation([15], 10),
      jumping:false,
      height: 32, width: 56,
      x:8, y:TILE_SIZE * 6 - TILE_SIZE * 0.25,
      y_velocity:0,

      reset:function() {

        this.alive = true;
        this.x = 8;

      },

      update:function() {

        game.player.y_velocity += 0.5;
        game.player.y += game.player.y_velocity;
        game.player.y_velocity *= 0.9;

      }

    },

    reset:function() {

      this.distance = 0;
      this.player.reset();

      /* Put all of our objects away. */
      this.object_manager.meteor_pool.storeAll();
      this.object_manager.smoke_pool.storeAll();
      this.object_manager.tarpit_pool.storeAll();

      this.speed = 3;

    }

  };

      ////////////////////
    //// INITIALIZE ////
  ////////////////////

  display.buffer.canvas.height = WORLD_HEIGHT;
  display.buffer.canvas.width  = WORLD_WIDTH;

  display.tile_sheet.image.src = "dino.png";
  display.tile_sheet.image.addEventListener("load", function(event) {

    display.tile_sheet.columns = this.width / TILE_SIZE;

    display.resize();

    game.engine.start();

  });

  window.addEventListener("resize", display.resize);
  window.addEventListener("mousedown", controller.onOff);
  window.addEventListener("mouseup", controller.onOff);
  window.addEventListener("touchstart", controller.onOff);
  window.addEventListener("touchend", controller.onOff);

})();


================================================
FILE: content/dominiques-doors/area0.json
================================================
{

  "message":"~The Tiny Closet~",
  "background_color":"#052623",
  "floor":62,
  "height":64,
  "width":128,

  "doors":[

    { "area":"area1.json", "x":48, "new_x":112 }

  ]

}


================================================
FILE: content/dominiques-doors/area1.json
================================================
{

  "message":"~The Main Lobby~",
  "background_color":"#003333",
  "floor":120,
  "height":128,
  "width":256,

  "doors":[

    { "area":"area2.json", "x":48, "new_x":48 },
    { "area":"area0.json", "x":112, "new_x":48 },
    { "area":"area3.json", "x":176, "new_x":16 }

  ]

}


================================================
FILE: content/dominiques-doors/area2.json
================================================
{

  "message":"~The Giant Pointless Room~",
  "background_color":"#006666",
  "floor":250,
  "height":256,
  "width":512,

  "doors":[

    { "area":"area1.json", "x":48, "new_x":48 }

  ]

}


================================================
FILE: content/dominiques-doors/area3.json
================================================
{

  "message":"~The Passageway~",
  "background_color":"#148977",
  "floor":60,
  "height":64,
  "width":200,

  "doors":[

    { "area":"area1.json", "x":16, "new_x":176 }

  ]

}


================================================
FILE: content/dominiques-doors/dominiques-doors.css
================================================
/* Frank Poth 01/14/2017 */

* {

  margin:0;
  padding:0;
  box-sizing:border-box;

}

html { height:100%; width:100%; }

body {

  align-content:space-around;
  align-items:space-around;
  color:#ffffff;
  background-color:#202830;
  display:grid;
  grid-template-columns:auto;
  grid-template-rows:auto auto;
  justify-items:center;
  min-height:100%;
  width:100%;

}

h1 { font-size: 3.0em; text-align:center; }


================================================
FILE: content/dominiques-doors/dominiques-doors.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <link href = "dominiques-doors.css" rel = "stylesheet" type = "text/css">

    <meta name = "viewport" content = "width=device-width">
    <meta name = "description" content = "Learn how to load JSON level data for a web game with JavaScript!">

    <title>PoP Vlog - Loading Levels!!!</title>

  </head>          

  <body>

    <h1>PoP Vlog - Loading Levels!!!</h1>

    <canvas></canvas>

    <script src = "dominiques-doors.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/dominiques-doors/dominiques-doors.js
================================================
// Frank Poth 01/14/2017

/* By studying this example program, you can learn how to load json levels, how
to animate sprites, and other basic game design techniques including: user Input
on the keyboard, some collision detection and response, image loading, and maybe
some other things, too. */

/* You may notice that in the largest room the dominique sprite looks a little weird.
There is a brown bar that flashes in front of her face when she walks to the left.
This is because of something called "texture bleeding" where a scaled image allows
pixels from around the cropped source region of the image to bleed into the desired
part of the source image. This is okay for cropping from large images, but for sprite
sheets it's not desireable. A way around this is to create individual canvases for
each sprite image and use those to draw rather than cutting frames from the original
sprite sheet. No bleeding can occur because there are no longer pixels around the
edges of the sprite image. */

(function() { "use strict";

  const Animation = function(frame_set, delay, mode = "loop") {

    this.count       = 0;
    this.delay       = delay;
    this.frame_index = 0;
    this.frame_set   = frame_set;
    this.frame_value = frame_set[0];
    this.mode = mode;

  };

  /* I expanded the Animation class to include play, loop, and rewind modes. They're
  all really simple, and basically they are the same thing with very minor changes
  dictating how the playhead or frame_index moves. */
  Animation.prototype = {

    constructor:Animation,

    change:function(frame_set, delay = this.delay) {

      if (frame_set != this.frame_set) {

        this.count       = 0;
        this.delay       = delay;
        this.frame_index = 0;
        this.frame_set   = frame_set;
        this.frame_value = frame_set[0];

      }

    },

    loop:function() {

      this.count ++;

      if (this.count >= this.delay) {

        this.count = 0;

        this.frame_index = (this.frame_index < this.frame_set.length - 1) ? this.frame_index + 1 : 0;
        this.frame_value = this.frame_set[this.frame_index];

      }

    },

    play:function() {

      this.count ++;

      if (this.count >= this.delay) {

        this.count = 0;

        if (this.frame_index < this.frame_set.length - 1) {

          this.frame_index ++;
          this.frame_value = this.frame_set[this.frame_index];

        }

      }

    },

    rewind:function() {

      this.count ++;

      if (this.count >= this.delay) {

        this.count = 0;

        if (this.frame_index > 0) {

          this.frame_index --;
          this.frame_value = this.frame_set[this.frame_index];

        }

      }

    },

    update:function() {

      this[this.mode]();

    }

  };

  /* I added offsets to the frames. This allows me to group my frames close together
  in the source image and save a lot of space in my image files. The offset is
  applied when drawing the image to the screen, ensuring that the sprite always looks
  centered and doesn't jump back and forth. */
  const Frame = function(x, y, width, height, offset_x = 0, offset_y = 0) {

    this.height   = height;
    this.offset_x = offset_x;
    this.offset_y = offset_y;
    this.width    = width;
    this.x        = x;
    this.y        = y;

  };

  Frame.prototype = { constructor:Frame };

  /* This simplifies creation of input keys. */
  const Input = function(active, state) {

    this.active = active;
    this.state  = state;

  };

  Input.prototype = {

    constructor:Input,

    update:function(state) {

      if (this.state != state) this.active = state;
      this.state  = state;

    }

  };

      //////////////////////
    //// GAME CLASSES ////
  //////////////////////

  const Door = function(x, y, area, new_x) {

    this.animation = new Animation(display.sprite_sheet.frame_set.door, 5, "play");
    this.area = area;
    this.new_x = new_x;
    this.x = x;
    this.y = y;

  };

  Door.prototype = {

    constructor:Door,

  };

      ///////////////
    //// LOGIC ////
  ///////////////

  var controller, display, game;

  controller = {

    down: new Input(false, false), left: new Input(false, false), right:new Input(false, false), up:new Input(false, false),

    keyDownUp:function(event) { event.preventDefault();

      var key_state = (event.type == "keydown") ? true : false;

      switch(event.keyCode) {

        case 37: controller.left.update(key_state); break;// left key
        case 38: controller.up.update(key_state); break;// up key
        case 39: controller.right.update(key_state); break;// right key
        case 40: controller.down.update(key_state); break;// down key

      }

    }

  };

  display = {

    buffer:document.createElement("canvas").getContext("2d"),
    context:document.querySelector("canvas").getContext("2d"),
    height_width_ratio:undefined,

    sprite_sheet: {

      frames:[new Frame(  0,  0, 27, 30), new Frame( 27,  0, 25, 30,  1),
              new Frame( 52,  0, 19, 29, -1,  1), new Frame( 71,  0, 19, 30, -1), new Frame(90,  0, 18, 30), new Frame(108,  0, 18, 31, 0, -1),
              new Frame(126,  0, 18, 30,  1), new Frame(144,  0, 18, 31,  1, -1), new Frame(162,  0, 19, 29, 2), new Frame(181,  0, 19, 30, 2),
              new Frame(200,  0, 32, 32), new Frame(232,  0, 32, 32), new Frame(264,  0, 32, 32), new Frame(296,  0, 32, 32), new Frame(328,  0, 32, 32), new Frame(360,  0, 32, 32), new Frame(392,  0, 32, 32)],

      frame_set: {

        dominique_idle:[0, 1],
        dominique_right:[2, 3, 4, 5],
        dominique_left:[6, 7, 8, 9],
        door:[10, 11, 12, 13, 14, 15, 16]

      },

      image:new Image()

    },

    render:function() {

      var frame;

      /* Draw the background. */
      this.buffer.fillStyle = game.area.background_color;
      this.buffer.fillRect(0, 0, game.area.width, game.area.height);

      /* Draw the floor. */
      this.buffer.fillStyle = "#373641";
      this.buffer.fillRect(0, game.area.floor - 3, game.area.width, game.area.height - game.area.floor + 3);

      /* Draw the doors. */
      for (let index = game.area.doors.length - 1; index > -1; -- index) {

        let door = game.area.doors[index];
        frame = this.sprite_sheet.frames[door.animation.frame_value];

        this.buffer.drawImage(this.sprite_sheet.image, frame.x, frame.y, frame.width, frame.height, door.x, door.y, frame.width, frame.height);

      }

      /* Draw Dominique. */
      frame = this.sprite_sheet.frames[game.dominique.animation.frame_value];

      this.buffer.drawImage(this.sprite_sheet.image, frame.x, frame.y, frame.width, frame.height, Math.round(game.dominique.x) + frame.offset_x * 0.5 - frame.width * 0.5, Math.round(game.dominique.y) + frame.offset_y * 0.5 - frame.height * 0.5, frame.width, frame.height);

      this.context.drawImage(this.buffer.canvas, 0, 0, game.area.width, game.area.height, 0, 0, this.context.canvas.width, this.context.canvas.height);

      this.context.fillStyle = "#ffffff";
      this.context.font = "20px Arial";
      this.context.fillText(game.area.message, 10, 20);

    },

    resize:function(event) {

      display.context.canvas.width = document.documentElement.clientWidth - 16;

      if (display.context.canvas.width > document.documentElement.clientHeight - 16) {

        display.context.canvas.width = document.documentElement.clientHeight - 16;

      }

      display.context.canvas.height = display.context.canvas.width * display.height_width_ratio;

      display.buffer.imageSmoothingEnabled = display.context.imageSmoothingEnabled = false;

      display.render();

    }

  };

  game = {

    area:undefined,

    dominique: {

      animation:new Animation(display.sprite_sheet.frame_set.dominique_idle, 15),
      half_height:15,
      half_width:10,
      jumping:false,
      velocity_x:0,
      velocity_y:0,
      x:100,
      y:100,

      collideWorld:function() {

        if (this.x - this.half_width < 0) {

          this.x = this.half_width;

        } else if (this.x + this.half_width > game.area.width) {

          if (game.area.message != "~The Passageway~") {

            this.x = game.area.width - this.half_width;

          } else if (this.x - this.half_width > game.area.width) {

            game.engine.stop();
            controller.right.active = false;

            game.loadArea("area0.json", function() {

              game.reset();

            });

            alert("Dominique escaped the weird program full of pointless rooms and doors. She went to a much better, more interesting program.");

          }

        }

        if (this.y + this.half_height > game.area.floor) {

          this.jumping = false;
          this.velocity_y = 0;
          this.y = game.area.floor - this.half_height;

        }

      },

      update:function() {

        this.velocity_y += 0.5;

        this.x += this.velocity_x;
        this.y += this.velocity_y;

        this.velocity_x *= 0.9;
        this.velocity_y *= 0.9;

      }

    },

    engine: {

      accumulated_time:window.performance.now(),
      frame_request:undefined,
      time_step:1000/60,

      loop:function(time_stamp) {

        game.engine.frame_request = window.requestAnimationFrame(game.engine.loop);

        if (controller.left.active) {

          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_left, 15);
          game.dominique.velocity_x -= 0.1;

        }

        if (controller.right.active) {

          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_right, 15);
          game.dominique.velocity_x += 0.1;

        }

        if (!controller.left.active && !controller.right.active) {

          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_idle, 15);

        }

        if (controller.up.active && !game.dominique.jumping) {

          controller.up.active = false;
          game.dominique.jumping = true;
          game.dominique.velocity_y = -5;

        }

        game.dominique.update();
        game.dominique.collideWorld();

        game.dominique.animation.update();

        for (let index = game.area.doors.length - 1; index > -1; -- index) {

          let door = game.area.doors[index];

          if (game.dominique.x > door.x && game.dominique.x < door.x + 32) {

            door.animation.mode = "play";

            if (controller.down.active) { controller.down.active = false;

              game.dominique.x = door.new_x + 1;
              game.loadArea(door.area, game.reset);

              return;

            }

          } else { door.animation.mode = "rewind"; }

          game.area.doors[index].animation.update();

        }

        display.render();

      },

      start:function() {

        this.accumulated_time = window.performance.now();
        this.frame_request = window.requestAnimationFrame(this.loop);

      },

      stop:function() {

        window.cancelAnimationFrame(this.frame_request);

      }

    },

    loadArea:function(url, callback) {

      var request, readyStateChange;

      request = new XMLHttpRequest();

      readyStateChange = function(event) {

        if (this.readyState == 4 && this.status == 200) {

          game.area = JSON.parse(this.responseText);

          callback();

          game.engine.start();

        }

      };

      request.addEventListener("readystatechange", readyStateChange);
      request.open("GET", url);
      request.send(null);

      game.engine.stop();

    },

    reset:function() {

      for (let index = game.area.doors.length - 1; index > -1; -- index) {

        let door = game.area.doors[index];

        game.area.doors[index] = new Door(door.x, game.area.floor - 32 - 3, door.area, door.new_x);

      }

      game.dominique.y = game.area.floor - game.dominique.half_height;
      game.dominique.velocity_x = 0;

      display.buffer.canvas.height = game.area.height;
      display.buffer.canvas.width = game.area.width;
      display.height_width_ratio = game.area.height / game.area.width;
      display.resize();

    }

  };

      ////////////////////
    //// INITIALIZE ////
  ////////////////////

  display.sprite_sheet.image.addEventListener("load", function(event) {

    game.loadArea("area0.json", function() {

      game.reset();

    });

  });

  display.sprite_sheet.image.src = "dominiques-doors.png";

  window.addEventListener("resize", display.resize);

  window.addEventListener("keydown", controller.keyDownUp);
  window.addEventListener("keyup", controller.keyDownUp);

})();


================================================
FILE: content/elements/elements.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <title></title>

  </head>

  <body style = "background-color:#404040; display:grid; justify-items:center;">

    <h1 style = "color:#ffffff; text-align:center">PoP Vlog</h1>

    <p id = "output" style = "color:#ffffff;">default</p>

    <input id = "input" type = "submit" value = "click me!">

    <script src = "elements.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/elements/elements.js
================================================
// Frank Poth 08/03/2017


var input = document.getElementById("input");
var output = document.getElementById("output");
var counter = 0;

var click = function(event) {

  counter += 1;
  output.innerHTML = "You clicked me " + counter + " times!";

};

input.addEventListener("click", click);


================================================
FILE: content/gjk/gjk.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <meta name = "viewport" content = "width=device-width">
    <meta name = "description" content = "An implementation of the GJK collision detection algorithm written in JavaScript.">
    <title>GJK Collision</title>
    <style>
      * { margin:0; overflow:hidden; padding:0; pointer-events:none; user-select:none; }
      html, body{ height:100%; width:100%; }
      body { background-color:#000000; display:grid; }
      #canvas { align-self:center; justify-self:center; }
    </style>

  </head>

  <body>

    <canvas id = "canvas"></canvas>

    <script type = "text/javascript">var o = document.querySelector("p");

      class ConvexShape2D {

        constructor() {}

        collide(shape, d) {

          var a = this.getExtremePoint(d[0], d[1]);// get the extreme point on this shape
          var b = shape.getExtremePoint(-d[0], -d[1]);// get the opposite extreme point on the other shape
          var p = [a[0] - b[0], a[1] - b[1]];// get the extreme point on the Minkowski Difference shape

          if (p[0] * d[0] + p[1] * d[1] < 0) return false;// if the origin is beyond p in direction d, return false
  
          var c = false;// cross product / simplex winding place holder; it doesn't matter how this is initialized because it will be reset when the simplex length is about to be 2
          var s = [[p[0], p[1]]];// the simplex now has one point in it

          d = [-p[0], -p[1]];// search in the direction of the origin next

          while(true) {

            a = this.getExtremePoint(d[0], d[1]);// get the extreme point on this shape
            b = shape.getExtremePoint(-d[0], -d[1]);// get the opposite extreme point on the other shape
            p = [a[0] - b[0], a[1] - b[1]];// get the extreme point on the Minkowski Difference shape

            if (p[0] * d[0] + p[1] * d[1] < 0) return false;// if the origin is beyond p in direction d, return false

            let tc = Boolean(s[0][0] * p[1] - s[0][1] * p[0] < 0);// the test cross product will be + / - depending on winding; It doesn't matter what the winding is so long as it is the same for every line segment in the simplex.

            if (s.length == 1) c = tc;// if there's only 1 point in the simplex, choose the current winding; this represents the winding of the first segment in the simplex
            else if (c == tc) {// if there is more than 1 point in the simplex and the winding is not changing with the addition of p (meaning that the winding is remaining the same for all segments in the simplex and the new segment)

              tc = Boolean(p[0] * s[1][1] - p[1] * s[1][0] < 0);// Now we do a cross check between the test point, p, and the start point of the simplex.
              
              if (c == tc) return true;// collision!
              else {// no collision and the winding has changed

                s.shift();
                tc = c;

              }

            } else {

              c = tc;
              s.pop();

            }

            if (tc) {

              d[0] = p[1] - s[0][1];
              d[1] = s[0][0] - p[0];

            } else {

              d[0] = s[0][1] - p[1];
              d[1] = p[0] - s[0][0];

            }

            s.unshift(p);

          }

        }

        getExtremePoint(vx, vy) {}

        moveTo() {}

      }

      class Circle extends ConvexShape2D {

        constructor(x, y, r) {

          super();

          this.x = x; this.y = y; this.r = r;

        }

        getExtremePoint(vx, vy) {
          
          var d = Math.atan2(vy, vx);

          vx = Math.cos(d) * this.r;
          vy = Math.sin(d) * this.r;

          return [this.x + vx, this.y + vy];

        }

        moveTo(x, y) {

          this.x = x; this.y = y;

        }

      }

      class Polygon extends ConvexShape2D {

        constructor(x, y, vertices) {

          super();

          this.x = this.y = 0;

          this.vertices = [...vertices];

          this.moveTo(x, y);

        }

        getExtremePoint(vx, vy) {

          var p = [this.vertices[0], this.vertices[1]];
          var ml = this.vertices[0] * vx + this.vertices[1] * vy

          for (var index = this.vertices.length - 2; index > 1; index -= 2) {

            let tl = this.vertices[index] * vx + this.vertices[index + 1] * vy;

            if (tl > ml) { 

              ml = tl
              p = [this.vertices[index], this.vertices[index + 1]];

            }

          }

          return p;

        }

        moveTo(x, y) {

          var vx = x - this.x;
          var vy = y - this.y;

          for (let index = this.vertices.length - 1; index > 0; index -= 2) {

            this.vertices[index - 1] += vx;
            this.vertices[index]     += vy;

          }

          this.x += vx;
          this.y += vy;

        }

      }

      var context = document.getElementById("canvas").getContext("2d", { alpha:false });

      var shapes = [
        new Circle( 100, 100, 20),
        new Polygon(100, 200, [0, -20, 20, 0, 20, 20, -20, 20, -20, 0]),
        new Polygon(100, 300, [-20, -20, 20, -20, 20, 20, -20, 20]),
        new Polygon(100, 400, [0, -20, 20, 20, -20, 20])
      ];

      var pointer = { x:0, y:0, down:false, getExtremePoint:function() { return [this.x, this.y]; } }

      var selected_shape = undefined;

      function drawShape(shape, c) {

        switch(shape.constructor) {

          case Circle: drawCircle(shape, c); break;
          case Polygon: drawPolygon(shape.vertices, c);

        }

      }

      function drawCircle(circle, c = "#0080f0") {

        context.beginPath();
        context.fillStyle = c;
        context.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2);
        context.closePath();
        context.fill();

      }

      function drawPolygon(vertices, c = "#0080f0") { 

        context.beginPath();
        context.fillStyle = c;
        context.moveTo(vertices[0], vertices[1]);
        
        for (let index = vertices.length - 1; index > 0; index -= 2) {

          context.lineTo(vertices[index - 1], vertices[index]);

        }

        context.closePath();
        context.fill()

      }

      function loop(time_stamp) {

        window.requestAnimationFrame(loop);

        context.fillStyle = "#ffffff";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        if (pointer.down && selected_shape == undefined) {

          for (let index = shapes.length - 1; index > -1; -- index) {

            let shape = shapes[index];

            if (shape.collide(pointer, [1, 0])) {
              
              selected_shape = shapes.splice(index, 1)[0];
              break;

            }

          }

        }

        let colliding = false;

        for (let index = shapes.length - 1; index > -1; -- index) {

          let shape = shapes[index];

          if (selected_shape && selected_shape.collide(shape, [1, 0])) {
            
            drawShape(shape, "#f08000");
            colliding = true;

          } else {
            
            drawShape(shape, "#0080f0");

          }

        }

        if (selected_shape) {

          selected_shape.moveTo(pointer.x, pointer.y);  
          if (colliding) drawShape(selected_shape, "#f08000");
          else drawShape(selected_shape, "#0080f0");
        
        }

        if (!pointer.down && selected_shape) {
          
          shapes.push(selected_shape);
          selected_shape = undefined;

        }

      }

      function mouseDownMoveUp(event) {

        var r = context.canvas.getBoundingClientRect();

        pointer.x = event.clientX - r.left;
        pointer.y = event.clientY - r.top;

        switch(event.type) {

          case "mousedown": pointer.down = true; break;
          case "mouseup": pointer.down = false;

        }

      }

      function resize(event) {

        context.canvas.height = document.documentElement.clientHeight - 16;
        context.canvas.width  = document.documentElement.clientWidth  - 16;

      }

      window.addEventListener("resize", resize);

      window.addEventListener("mousedown", mouseDownMoveUp);
      window.addEventListener("mousemove", mouseDownMoveUp);
      window.addEventListener("mouseup", mouseDownMoveUp);

      resize();
      loop();
    
    </script>

  </body>

</html>

================================================
FILE: content/hello-world/hello.html
================================================
<DOCTYPE html>

<html>

  <head>

    <title>Hello, world!</title>

  </head>

  <body>

    <h1>Hello, world!</h1>

    <script type = "text/javascript">

      alert("Hello, world!");
      console.log("Hello, world (from the console)!");

    </script>

  </body>

</html>


================================================
FILE: content/hit-the-wall/hit-the-wall.css
================================================
/* Frank Poth 11/16/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

html {

  height:100%;
  width:100%;

}

body {

  align-content:space-around;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  grid-template-columns:auto;
  grid-template-rows:auto auto auto auto;
  height:100%;
  justify-content:space-around;
  text-align:center;
  width:100%;

}

canvas {

  background-color:#303840;
  justify-self:center;

}


================================================
FILE: content/hit-the-wall/hit-the-wall.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <link href = "hit-the-wall.css" rel = "stylesheet" type = "text/css">
    <meta name = "description" content = "PoP Vlog 20 - Learn how to do basic collision detection and response in a 2d tile based game world.">
    <meta name = "veiwport" content = "width=device-width">

    <title>PoP Vlog - Hit The Wall</title>

  </head>

  <body>

    <h1>PoP Vlog - Hit The Wall</h1>

    <p>&nbsp;</p> <!-- the output p element -->

    <canvas></canvas>

    <p>This example requires a keyboard.</p>

    <script src = "hit-the-wall.js" type = "text/javascript"></script>

  </body>

</html>


================================================
FILE: content/hit-the-wall/hit-the-wall.js
================================================
// Frank Poth 11/16/2017

(function() { "use strict"

  // the three main components of the example
  var controller, display, game;

  // a basic controller object that handles key input
  controller = {

    left:false,
    right:false,
    up:false,

    keyUpDown:function(event) {

      var key_state = (event.type == "keydown")?true:false;

      switch(event.keyCode) {

        case 37: controller.left = key_state; break; // left key
        case 38: controller.up = key_state; break; // up key
        case 39: controller.right = key_state; break; // right key

      }

    }

  };

  // draws everything and handles html elements
  display = {

    buffer:document.createElement("canvas").getContext("2d"),
    context:document.querySelector("canvas").getContext("2d"),
    output:document.querySelector("p"),

    render:function() {

      for (let index = game.world.map.length - 1; index > -1; -- index) {

        this.buffer.fillStyle = (game.world.map[index] > 0)?("#0099" + game.world.map[index] + "f"):"#303840";
        this.buffer.fillRect((index % game.world.columns) * game.world.tile_size, Math.floor(index / game.world.columns) * game.world.tile_size, game.world.tile_size, game.world.tile_size);

      }

      this.buffer.fillStyle = game.player.color;
      this.buffer.fillRect(game.player.x, game.player.y, game.player.width, game.player.height);

      this.context.drawImage(this.buffer.canvas, 0, 0, this.buffer.canvas.width, this.buffer.canvas.height, 0, 0, this.context.canvas.width, this.context.canvas.height);

    },

    resize:function(event) {

      var client_height = document.documentElement.clientHeight;

      display.context.canvas.width = document.documentElement.clientWidth - 32;

      if (display.context.canvas.width > client_height) {

        display.context.canvas.width = client_height;

      }

      display.context.canvas.height = Math.floor(display.context.canvas.width * 0.625);

      display.render();

    }

  };

  // here's where things get interesting
  // the game object holds all of our awesome game logic
  game = {

    // there's something different about the player object, and its old_x and
    // old_y. these variables keep track of the last position the player occupied
    player: {

      color:"#ff9900",
      height:32,
      jumping:false,
      old_x:160,// used for tracking last position for collision detection
      old_y:160,
      velocity_x:0,
      velocity_y:0,
      width:24,
      x:160,
      y:90

    },

    // the world object holds information about our tile map
    world: {

      columns:8,// just some basic info. no worries
      rows:5,
      tile_size:40,

      map:[0,0,0,0,0,0,0,0,// I went with a smaller map this time
           0,0,0,0,0,0,0,0,// 0s represent walkable tiles and everything else
           1,0,0,0,0,0,0,2,// represents a collision tile or wall tile
           3,0,0,4,0,0,2,5,// the different numbers correspond to different
           5,5,5,5,5,5,5,5]// collision shapes.

    },

    // the collision object is used to handle narrow phase collision detection
    // and response. Broad phase collision detection is just determining if the
    // player is standing on a non 0 tile in the map, narrow phase is handled
    // here, where the player's exact position is tested against the specific
    // collision boundaries of each type of tile. In this example there are 5
    // types of collision tile
    collision: {

      // the reason these functions are indexed with numbers is because they
      // correspond directly to tile values in the map array. For instance, this
      // function handles collision for any tile in the map with a value of 1
      // it handles collision detection and response on the specified player object
      // at the specified row and column in the tile map. the 1 tile collision
      // shape has a flat top and a flat right side, so only test for collision
      // and do response on those sides.
      1:function(object, row, column) {

        /* Basically these indexed functions are like routing functions. They
        make it easy to connect a collision shape with a value in the tile map.
        The real collision code happens in these inner functions. The great thing
        about this approach is that it's extremely modular, so you can mix and
        match collision functions and easily introduce new ones. */

        /* Depending on whether you want to do y or x first collision is as simple
        as calling the collision function for a specific axis before the other.
        In a platformer where players are always falling down due to gravity, it's
        a good idea to do y collision detection first. */

        if (this.topCollision(object, row)) { return; }// if no top collision
        this.rightCollision(object, column);           // try right side collision

      },

      // the 2 tile type has a top and a left side to collide with
      2:function(object, row, column) {

        if (this.topCollision(object, row)) { return; }
        this.leftCollision(object, column);

      },

      // the 3 tile type only blocks movement through the right side
      3:function(object, row, column) {

        this.rightCollision(object, column);

      },

      // the 4 tile type has collision on all sides but the bottom
      4:function(object, row, column) {

        if (this.topCollision(object, row)) { return; }// you only want to do one
        if (this.leftCollision(object, column)) { return; }// of these collision
        this.rightCollision(object, column);// responses. that's why there are if statements

      },

      // the 5 tile only does collision if the object falls through the top
      5:function(object, row, column) {

        this.topCollision(object, row);

      },

      /* Here are the actual collision detection and response functions. The concept
      I am working off of with this example is that all tiles have 4 sides. Rather
      than doing collision for all of them in the same function, I want to mix and
      match collision methods for each side. By doing it this way I can create all
      sorts of collision shapes and resuse my side specific collision code. This
      works extremely well, and the only downside is having to be more specific
      when you lay out your level maps because each tile has a specific purpose.
      For example, a floor tile only tests for collision on its top side and a wall
      tile will only test for collision on the left and/or right side. It is a small
      price to pay for awesome and smooth collision detection with basically no restriction on
      tile based collision shapes. You can have tiles where one side is a slope and the
      other is a curve or a flat side, for instance. That's pretty sweet. */

      /* This function handles collision with a rightward moving object. Another
      design requirement to use this method is that objects must have a record of
      their current and last physical position. A collision can only occur when a
      player enters into a collision shape through its boundary. It's foolproof so
      long as you always spawn your players on empty tiles and not in the walls. */
      leftCollision(object, column) {

        if (object.velocity_x > 0) {// If the object is moving right

          var left = column * game.world.tile_size;// calculate the left side of the collision tile

          if (object.x + object.width * 0.5 > left && object.old_x <= left) {// If the object was to the right of the collision object, but now is to the left of it

            object.velocity_x = 0;// Stop moving
            object.x = object.old_x = left - object.width * 0.5 - 0.001;// place object outside of collision
            // the 0.001 is just to ensure that the object is no longer in the same tile space as the collision tile
            // due to the way object tile position is calculated, moving the object to the exact boundary of the collision tile
            // would not move it out if its tile space, meaning that another collision with an adjacent tile might not be detected in another broad phase check

            return true;

          }

        }

        return false;

      },

      // these are all basically the same concept as the leftCollision function,
      // only for the different sides.

      rightCollision(object, column) {

        if (object.velocity_x < 0) {

          var right = (column + 1) * game.world.tile_size;

          if (object.x + object.width * 0.5 < right && object.old_x + object.width * 0.5 >= right) {

            object.velocity_x = 0;
            object.old_x = object.x = right - object.width * 0.5;

            return true;

          }

        }

        return false;

      },

      topCollision(object, row) {

        if (object.velocity_y > 0) {

          var top = row * game.world.tile_size;

          if (object.y + object.height > top && object.old_y + object.height <= top) {

            object.jumping = false;
            object.velocity_y = 0;
            object.old_y = object.y = top - object.height - 0.01;

            return true;

          }

        }

        return false;

      }

    },

    // Here's the game loop, where it all goes down!!!
    loop:function() {

      // get and use keyboard input
      if (controller.left) {

        game.player.velocity_x -= 0.25;

      }

      if (controller.right) {

        game.player.velocity_x += 0.25;

      }

      if (controller.up && !game.player.jumping) {

        game.player.velocity_y = -16;
        game.player.jumping = true;

      }

      game.player.velocity_y += 1; // add gravity

      game.player.old_x = game.player.x;// store the last position of the player
      game.player.old_y = game.player.y;// before we move it on this cycle

      game.player.x += game.player.velocity_x;// move the player's current position
      game.player.y += game.player.velocity_y;

      // do collision detection with the level boundaries so the player can't leave
      // the screen. Nothing you haven't seen before...
      if (game.player.x < 0) {

        game.player.velocity_x = 0;
        game.player.old_x = game.player.x = 0;

      } else if (game.player.x + game.player.width > display.buffer.canvas.width) {

        game.player.velocity_x = 0;
        game.player.old_x = game.player.x = display.buffer.canvas.width - game.player.width;

      }

      if (game.player.y < 0) {

        game.player.velocity_y = 0;
        game.player.old_y = game.player.y = 0;

      } else if (game.player.y + game.player.height > display.buffer.canvas.height) {

        game.player.velocity_y = 0;
        game.player.old_y = game.player.y = display.buffer.canvas.height - game.player.height;

      }

      // NOW FOR SOME GOOD STUFF!!! Here we do broadphase collision detection by checking
      // which tile value the player is standing on. If it is anything but 0 we have a possible collision.

      // calculate the player's x and y tile position in the tile map
      var tile_x = Math.floor((game.player.x + game.player.width * 0.5) / game.world.tile_size);
      var tile_y = Math.floor((game.player.y + game.player.height) / game.world.tile_size);
      // get the value at the tile position in the map
      var value_at_index = game.world.map[tile_y * game.world.columns + tile_x];

      // do some output so we can see it all in action
      display.output.innerHTML = "tile_x: " + tile_x + "<br>tile_y: " + tile_y + "<br>map index: " + tile_y + " * " + game.world.columns + " + " + tile_x + " = " + String(tile_y * game.world.columns + tile_x) + "<br>tile value: " + game.world.map[tile_y * game.world.columns + tile_x];

      // if it's not an empty tile, we need to do narrow phase collision detection and possibly response!
      if (value_at_index != 0) {

        // simply call one of the routing functions in the collision object and pass
        // in values for the collision tile's location in grid/map space
        game.collision[value_at_index](game.player, tile_y, tile_x);

      }

      // This might be confusing at first, but we actually have to do this twice, since
      // we are using a player object with a single point of contact, which is its bottom middle.
      // say we handled a collision with the floor in the check above and now the player object is
      // moved up above that tile. There's still a posibility that the player was moved into
      // an adjacent wall tile! We need to recalculate the new tile space the player is in
      // and then check to see if there is another collision. Don't worry, we only need
      // to do this twice. Once for the x axis, and one for the y axis. With this
      // collision method they could happen in either order, but they will both be handled.

      tile_x = Math.floor((game.player.x + game.player.width * 0.5) / game.world.tile_size);
      tile_y = Math.floor((game.player.y + game.player.height) / game.world.tile_size);
      value_at_index = game.world.map[tile_y * game.world.columns + tile_x];

      if (value_at_index != 0) {

        game.collision[value_at_index](game.player, tile_y, tile_x);

      } // and that's it! You checked twice and resolved any collisions with the map!

      game.player.velocity_x *= 0.9;// apply some friction to the player's velocity
      game.player.velocity_y *= 0.9;// the reason we do this after the collision code
      // is because we use the players velocity in the collision code and don't want to change it before now.
      // it probably doesn't matter, though. You could also calculate the velocity for collision by
      // subtracting the players last position from its current position, that never fails.

      display.render();

      window.requestAnimationFrame(game.loop);

    }

  };

  // basic setup and initialization
  display.buffer.canvas.height = 200;
  display.buffer.canvas.width = 320;

  window.addEventListener("resize", display.resize);
  window.addEventListener("keydown", controller.keyUpDown);
  window.addEventListener("keyup", controller.keyUpDown);

  display.resize();

  game.loop();

})();


================================================
FILE: content/hitbox/hitbox.html
================================================
<!DOCTYPE html>

<html>

  <head>
        
    <meta name = "description" content = "Interactive example of player vs player hitbox collision with full JavaScript source code.">
    <meta name = "viewport" content = "width=device-width user-scalable=no">
    
    <style>
      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; user-select:none; }
      body, html { height:100%; width:100%; }
      body { background-color:#202830; display:grid; }
      a { border-radius:16px; border-style:solid; border-width:4px; border-color:#202830; color:#202830; cursor:pointer; font-size:1.5em; font-weight:800; padding:8px; pointer-events:all; position:fixed; top:24px; left:24px; }
      canvas { align-self:center; justify-self:center; }
    </style>
    
    <title>Hitbox</title>
    
  </head>
  
  <body>

    <a>freeze</a>

    <canvas></canvas>

    <script type = "text/javascript">

      /* You will notice tunneling in this example program. The player falling through platforms and being able to jump through platforms laid out a certain way is the result of tunneling brought on by the nature of the collision detection logic itself as well as sorting order's effect on which blocks are tested for collision first. When you see a "glitch" it's actually working fine. There are simply some limitations to this collision method. That being said, this example isn't ideal for this collision logic. Generally it is best for 1 on 1 collisions between players or you could use it for a static world that was laid out specifically with tunnelling in mind. */

      /* This is the base class for both the platforms and the player. It keeps track of its current and old position precisely by each corner point to avoid rounding errors. I found that if I just kept track of the top left corner and calculated right and bottom by adding width and height, I would sometimes get inaccurate results. The old positions are never calculated. They are explicitly set to equal whatever the current position is on each frame. This is important for accuracy. */
      class Rectangle {

        constructor(l, t, w, h) { // left, top, width, height

          this.l = this.ol = l; // left and old left
          this.r = this.or = l + w; // right and old right
          this.t = this.ot = t; // top and old top
          this.b = this.ob = t + h; // bottom and old bottom
          this.w = w; // width
          this.h = h; // height
          this.vx = this.vy = 0; // velocity x and y

        }

        /* We can do calculations when we are setting the current position because the current position is new. Avoiding rounding errors isn't as important here. It is important when saving the current position to the old position, however, because the values must not change for the sake of accuracy. */
        setBottom(b) { this.b = b; this.t = b - this.h; }
        setLeft(l)   { this.l = l; this.r = l + this.w; }
        setRight(r)  { this.r = r; this.l = r - this.w; }
        setTop(t)    { this.t = t; this.b = t + this.h; }

      }

      /* Ideally you would not use this method for collision with platforms since tunnelling can occur around the edges. This is best used for player vs player hitbox collision where the occasional tunneling isn't going to cause the player to fall to his death in a notably unfair fashion, but, that being said, reducing the speed of the player will greatly reduce tunneling, and this collision technique is very stable so long as everything is moving relatively slowly (not ideal for bullets). */ 
      class Platform extends Rectangle {

        constructor(l, t, w, h) {

          super(l, t, w, h);

          this.d = Math.random() * Math.PI * 2; // initial movement direction
          this.rotation = Math.random() * Math.PI * 2; // rotation for sine wave movement

          this.frozen = false;

        }

        collideBoundaries(l, r, t, b) { // collide with the walls

          if (this.l < l) {

            this.d = Math.atan2(Math.sin(this.d), Math.cos(this.d) * -1);
            this.vx *= -1;
            this.setLeft(l);

          } else if (this.r > r) {

            this.d = Math.atan2(Math.sin(this.d), Math.cos(this.d) * -1);
            this.vx *= -1;
            this.setRight(r);

          }

          if (this.t <= t) {

            this.d = Math.atan2(Math.sin(this.d) * -1, Math.cos(this.d));
            this.vy *= -1;
            this.setTop(t);

          } else if (this.b > b) {

            this.d = Math.atan2(Math.sin(this.d) * -1, Math.cos(this.d));
            this.vy *= -1;
            this.setBottom(b);

          }

        }

        update() {

          if (!this.frozen) {
            
            this.vx = Math.cos(this.d); // move in direction
            this.vy = Math.sin(this.d);

            this.rotation += 0.05;
            this.vy += Math.sin(this.rotation); // sine wave motion

          }

          this.ob = this.b; // update the old positions to the current positions
          this.ol = this.l;
          this.or = this.r;
          this.ot = this.t;

          this.l += this.vx; // update the current positions to the new positions
          this.t += this.vy;
          this.r = this.l + this.w;
          this.b = this.t + this.h;
          
        }

      }

      class Player extends Rectangle {

        constructor(l, t, w, h) {

          super(l, t, w, h);

          this.jumping = true;

        }

        collideRectangle(rectangle) {

          if (this.b < rectangle.t || this.t > rectangle.b || this.l > rectangle.r || this.r < rectangle.l) return;

          /* You can only collide with one side at a time, so "else if" is just fine. You don't need to separate the checks for x and y. Only one check can be true, so only one needs to be done. Once it's found, the other's don't need to be done. */
          if (this.b >= rectangle.t && this.ob < rectangle.ot) {

            this.setBottom(rectangle.t - 0.1);
            this.vy = rectangle.vy; // the platform moves the player with it after collision...
            this.jumping = false;

          } else if (this.t <= rectangle.b && this.ot > rectangle.ob) {

            this.setTop(rectangle.b + 0.1);
            this.vy = rectangle.vy; // ... regardless of what side the player collides with

          } else if (this.r >= rectangle.l && this.or < rectangle.ol) {

            this.setRight(rectangle.l - 0.1);
            this.vx = rectangle.vx;

          } else if (this.l <= rectangle.r && this.ol > rectangle.or) {

            this.setLeft(rectangle.r + 0.1);
            this.vx = rectangle.vx;

          }

        }

        update(g, f) { // gravity and friction

          this.vy += g; // you can make updates to velocity before or after the position update

          this.vx *= f; // I choose before so there isn't one frame of inactivity on the first cycle
          this.vy *= f;

          this.ob = this.b; // update the old positions to the current positions
          this.ol = this.l;
          this.or = this.r;
          this.ot = this.t;

          this.l += this.vx; // update the current positions to the new positions
          this.t += this.vy;
          this.r = this.l + this.w;
          this.b = this.t + this.h;

        }

      }

      var context = document.querySelector("canvas").getContext("2d");

      var gravity = 1;
      var friction = 0.9;

      var player   = new Player(context.canvas.width * 0.25, 0, 32, 32, "#0080f0");
      var platforms = [];

      var pointer = { x:player.l, down:false };
      
      function getFloor() {

        return context.canvas.height * 0.9;

      }

      function collideFloor(player) {

        var floor = getFloor();

        if (player.b > floor) {

          player.setBottom(floor);
          player.vy      = 0;
          player.jumping = false;

        }

      }

      function loop(time_stamp) {

        update();
        render();

        window.requestAnimationFrame(loop);

      }

      function render() {

        context.fillStyle = "#f0d8c0";
        context.fillRect(0, 0, context.canvas.width, context.canvas.height);

        context.fillStyle = "#202830";
        context.fillRect(0, getFloor(), context.canvas.width, 8);

        context.fillStyle = "#0080f0";
        context.fillRect(player.l, player.t, player.w, player.h);

        context.font = "20px Arial";

        for (let index = platforms.length - 1; index > -1; -- index) {

          let platform = platforms[index];

          context.fillStyle = "#f08000";
          context.fillRect(platform.l, platform.t, platform.w, platform.h);

        }

      }

      function update() {

        player.vx += (pointer.x - player.l - player.w * 0.5) * 0.01;

        if (pointer.down && !player.jumping) {

          player.jumping = true;
          player.vy -= player.h + 1;
          pointer.down = false;

        }

        player.update(gravity, friction);

        collideFloor(player);

        for (let index = platforms.length - 1; index > -1; -- index) {

          let platform = platforms[index]; 

          platform.update();

          platform.collideBoundaries(0, context.canvas.width, 0, getFloor());

          player.collideRectangle(platform);

        }

      }

        /////////////////////////
       //// EVENT LISTENERS ////
      /////////////////////////

      function mouseDownUp(event) { event.preventDefault();
      
        pointer.down = event.type == "mousedown" ? true : false;
      
      }

      function mouseMove(event) { event.preventDefault();

        pointer.x = event.clientX - context.canvas.getBoundingClientRect().left;

      }

      function touchEndStart(event) { event.preventDefault();
      
        pointer.down = event.type == "touchstart" ? true : false;

        if (pointer.down) pointer.x = event.targetTouches[0].clientX - context.canvas.getBoundingClientRect().left;
      
      }

      function touchMove(event) { event.preventDefault();
      
        pointer.x = event.targetTouches[0].clientX - context.canvas.getBoundingClientRect().left;
      
      }

      function resize(event) {

        context.canvas.height = document.documentElement.clientHeight - 32;
        context.canvas.width  = document.documentElement.clientWidth  - 32;

      }

        ////////////////////
       //// INITIALIZE ////
      ////////////////////

      resize();

      // consolidate event listeners that use the same event handler (probably overkill)
      ["mousedown", "mouseup"].map(type => window.addEventListener(type, mouseDownUp));
      ["touchend", "touchstart"].map(type => window.addEventListener(type, touchEndStart));

      window.addEventListener("mousemove",  mouseMove);
      window.addEventListener("touchmove",  touchMove);
      window.addEventListener("resize",     resize);
      window.addEventListener("contextmenu", (event) => { event.preventDefault(); });

      ["click", "touchstart"].map(type => document.querySelector("a").addEventListener(type, (event) => { event.preventDefault();
        
        for (let index = platforms.length - 1; index > -1; -- index) {

          let platform = platforms[index];

          platform.frozen = !platform.frozen;

          if (platform.frozen) platform.vx = platform.vy = 0;

        }

      }));

      (() => { // change scope to keep these variables out of the global scope

        var w = context.canvas.width;
        var h = context.canvas.height;

        var columns = Math.floor(w / 128);
        var rows    = Math.floor(h / 128);

        for (let column = columns; column > 0; column --) {

          for (let row = rows; row > 0; row --) {

            platforms.push(new Platform(column * 128 - 64, row * 128 - 64, Math.random() * (w / columns), Math.random() * (h / rows) * 0.5 + 1));

          }

        }

      })();

      loop();
        
    </script>

  </body>

</html>

================================================
FILE: content/https-server/index.css
================================================
/* Frank Poth 10/16/2017 */

* {

  box-sizing:border-box;
  margin:0;
  padding:0;

}

html {

  height:100%;
  width:100%;

}

body {

  align-content:center;
  background-color:#202830;
  color:#ffffff;
  display:grid;
  height:100%;
  justify-content:space-around;
  text-align:center;
  width:100%;

}


================================================
FILE: content/https-server/index.html
================================================
<!DOCTYPE html>

<html>

  <head>

    <title>HTTPS Server!</title>

    <meta name = "viewport" content = "width=device-width">

    <link href = "/index.css" rel = "stylesheet" type = "text/css">

  </head>

  <body>

    <h1>PoP Vlog - HTTPS Server!</h1>
    <br>
    <p>Is there a green https indicator in your address bar? If so, this page was served over a secure server using a valid and up to date certificate chain!</p>

  </body>

</html>


================================================
FILE: content/https-server/server.js
================================================
// Frank Poth 10/16/2017

(function() {

  var fs, https, mimetypes, options, path, server;

  fs = require("fs"); // file system
  https = require("https"); // creates an https server
  path = require("path"); // used for working with url paths

  // used to create response headers
  /* If the user requests a .css file, we want to ensure we attach "text/css" to
  our response header, this way the browser knows how to handle it. */
  mimetypes = {

    "css":"text/css",
    "html":"text/html",
    "ico":"image/ico",
    "jpg":"image/jpeg",
    "js":"text/javascript",
    "json":"application/json",
    "png":"image/png"

  };

  options = {

    pfx: fs.readFileSync("ssl/crt.pfx"),
    passphrase: "password"

  };

  // Start a secure server that uses the credentials in ssl/crt.pfx
  server = https.createServer(options, function(request, response) {

    /* When requesting the homepage of a website, we usually only type
    www.mysite.com, but the server returns www.mysite.com/index.html. To make
    it easier for users to access our site, we add "/index.html" to their url
    so the user doesn't have to type out the whole address of our home page. */

    // If the url is empty
    if (request.url == "" || request.url == "/") {

      // The user is requesting the home page of the website, so give it to them
      request.url = "index.html";

    }

    // Next we read the file at the requested url and write it to the document.
    /* __dirname is just the base directory of your website, so if your website
    is www.coolsite.com, then __dirname is www.coolsite.com. When you put it all
    together it looks like www.coolsite.com/index.html or whatever the requested
    url is */
    fs.readFile(__dirname + "/" + request.url, function(error, content) {

      if (error) { // if there is an error reading the requested url

        console.log("Error: " + error); // output it to the console

      } else { // else, there is no error, write the file contents to the page

        // 200 is code for OK, and the second parameter is our content header
        response.writeHead(200, {'Content-Type':mimetypes[path.extname(request.url).split(".")[1]]});
        response.write(content); // write that content to our response object

      }

      response.end(); // This will send our response object to the browser

    });

  });

  server.listen("2468", "192.168.0.101", function() {

    console.log("Server started!");

  }); // listen on 192.168.0.101:2468

})();


================================================
FILE: content/https-server/ssl/crt.cnf
================================================
[req]
days                   = 180
serial                 = 1
distinguished_name     = req_distinguished_name
x509_extensions        = v3_ca

[req_distinguished_name]
countryName            = Country
stateOrProvinceName    = State
localityName           = Locality
organizationName       = Organizational Name
organizationalUnitName = Organizational Unit Name
commonName             = Common Name (Domain Name)
emailAddress           = Email Address


[ v3_ca ]
# The extentions to add to a self-signed cert
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer:always

# THIS IS VERY IMPORTANT IF YOU WANT TO USE THIS CERTIFICATION AS AN AUTHORITY!!!
basicConstraints       = CA:TRUE

keyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign
subjectAltName         = DNS:192.168.0.101, IP:192.168.0.101
issuerAltName          = issuer:copy


================================================
FILE: content/https-server/ssl/csr.cnf
================================================
[req]

days                   = 180
distinguished_name     = req_distinguished_name
req_extensions         = v3_req

[req_distinguished_name]

countryName            = Country
stateOrProvinceName    = State
localityName           = Locality
organizationName       = Organizational Name
organizationalUnitName = Organizational Unit Name
commonName             = Common Name (Domain Name)
emailAddress           = Email Address

[ v3_req ]

# Extensions to add to a certificate request
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign


================================================
FILE: content/https-server/ssl/make-crt.sh
================================================
#!/bin/bash
 
echo ""
echo "Generate a self signed certificate:"
echo "Enter certificate name (example: crt.crt):"
read crt_path
echo "Enter path to certificate configuration file (example: crt.cnf):"
read cnf_path
echo "Enter path to private key file (example: key.key):"
read key_path
echo "Enter path to CSR file (example: csr.csr):"
read csr_path
echo "Enter number of days until expiration (example: 365):"
read days
echo ""

# certificate extensions must be stored in the v3_ca section in the configuration file
if openssl x509 -req -days $days -in $csr_path -signkey $key_path -out $crt_path -extfile $cnf_path -extensions v3_ca
then

  echo ""
  echo "Created "$crt_path" in "$PWD"/"$crt_path

else

  echo ""
  echo "Could not generate certificate due to error!"

fi

echo ""


================================================
FILE: content/https-server/ssl/make-csr.sh
================================================
#!/bin/bash
 
echo ""
echo "Generate a Certificate Signing Request (CSR) file:"
echo "Enter CSR name (example: csr.csr):"
read csr_path
echo "Enter path to CSR configuration file (example: csr.cnf):"
read cnf_path
echo "Enter path to private key file (example: key.key):"
read key_path
echo ""

if openssl req -config $cnf_path -new -key $key_path -inform PEM -out $csr_path -outform PEM
then

  echo ""
  echo "Created "$csr_path" in "$PWD"/"$csr_path

else

  echo ""
  echo "Could not generate CSR due to error!"

fi

echo ""


================================================
FILE: content/https-server/ssl/make-key.sh
================================================
#!/bin/bash
 
echo ""
echo "Generate a private Key file in PEM format:"
echo "Enter key name (example: key.key):"
read key_path
echo ""

if  openssl genpkey -algorithm RSA -out $key_path -outform PEM -pkeyopt rsa_keygen_bits:4096
then

  echo ""
  echo "Created "$key_path" in "$PWD"/"$key_path

else

  echo ""
  echo "Could not create "$key_path" due to error!"

fi

echo ""


================================================
FILE: content/https-server/ssl/make-pfx.sh
================================================
#!/bin/bash

echo ""
echo "Generate a Public Key Cryptography Standards # 12 ( PKCS#12 ) file:"
echo "Enter certificate name (example: crt.pfx):"
read pfx_path
echo "Enter path to private key file (example: key.key):"
rea
Download .txt
gitextract_49aaihe4/

├── content/
│   ├── 2018/
│   │   └── minkowski-difference/
│   │       └── minkowski-difference.html
│   ├── animation/
│   │   ├── animation.css
│   │   ├── animation.html
│   │   └── animation.js
│   ├── animation-game-loop/
│   │   ├── animation.css
│   │   ├── animation.html
│   │   └── animation.js
│   ├── better-tile/
│   │   ├── better-tile.html
│   │   └── better-tile.js
│   ├── better-tile-graphics/
│   │   ├── better-tile-graphics.html
│   │   └── better-tile-graphics.js
│   ├── blit/
│   │   ├── blit.css
│   │   ├── blit.html
│   │   └── blit.js
│   ├── bouncing-polygons/
│   │   └── bouncing-polygons.html
│   ├── calculator/
│   │   ├── calculator.css
│   │   ├── calculator.html
│   │   └── calculator.js
│   ├── canvas/
│   │   ├── canvas.css
│   │   ├── canvas.html
│   │   └── canvas.js
│   ├── circle-collision-detection/
│   │   └── circle-collision-detection.html
│   ├── circle-collision-response/
│   │   └── circle-collision-response.html
│   ├── collision/
│   │   ├── collision.css
│   │   ├── collision.html
│   │   └── collision.js
│   ├── control/
│   │   ├── control.css
│   │   ├── control.html
│   │   └── control.js
│   ├── cube/
│   │   └── cube.html
│   ├── dino/
│   │   ├── dino.css
│   │   ├── dino.html
│   │   └── dino.js
│   ├── dominiques-doors/
│   │   ├── area0.json
│   │   ├── area1.json
│   │   ├── area2.json
│   │   ├── area3.json
│   │   ├── dominiques-doors.css
│   │   ├── dominiques-doors.html
│   │   └── dominiques-doors.js
│   ├── elements/
│   │   ├── elements.html
│   │   └── elements.js
│   ├── gjk/
│   │   └── gjk.html
│   ├── hello-world/
│   │   └── hello.html
│   ├── hit-the-wall/
│   │   ├── hit-the-wall.css
│   │   ├── hit-the-wall.html
│   │   └── hit-the-wall.js
│   ├── hitbox/
│   │   └── hitbox.html
│   ├── https-server/
│   │   ├── index.css
│   │   ├── index.html
│   │   ├── server.js
│   │   └── ssl/
│   │       ├── crt.cnf
│   │       ├── crt.pfx
│   │       ├── csr.cnf
│   │       ├── make-crt.sh
│   │       ├── make-csr.sh
│   │       ├── make-key.sh
│   │       └── make-pfx.sh
│   ├── indexed-db/
│   │   ├── index.css
│   │   └── index.html
│   ├── inheritance/
│   │   ├── inheritance.html
│   │   └── inheritance.js
│   ├── inventory/
│   │   └── inventory.html
│   ├── ipo/
│   │   ├── components/
│   │   │   ├── input.js
│   │   │   ├── main.js
│   │   │   ├── output.js
│   │   │   └── processor.js
│   │   └── ipo.html
│   ├── json/
│   │   ├── json.html
│   │   ├── json.js
│   │   └── json.json
│   ├── load-image/
│   │   ├── load-image.css
│   │   ├── load-image.html
│   │   └── load-image.js
│   ├── multiple-inheritance/
│   │   ├── multiple-inheritance.html
│   │   └── multiple-inheritance.js
│   ├── objects-and-vars/
│   │   ├── objects.html
│   │   └── objects.js
│   ├── offline-web-app/
│   │   ├── manifest.json
│   │   ├── server.js
│   │   ├── ssl/
│   │   │   └── crt.pfx
│   │   ├── web-app-service.js
│   │   ├── web-app.css
│   │   └── web-app.html
│   ├── pagination/
│   │   ├── article1.txt
│   │   ├── article2.txt
│   │   ├── article3.txt
│   │   ├── article4.txt
│   │   ├── article5.txt
│   │   ├── pagination.css
│   │   ├── pagination.html
│   │   └── paginator.js
│   ├── particle-pool/
│   │   └── particle-pool.html
│   ├── platform/
│   │   └── platform.html
│   ├── platformer-ai/
│   │   └── platformer-ai.html
│   ├── polygon/
│   │   └── polygon.html
│   ├── polygon-rotation/
│   │   └── polygon-rotation.html
│   ├── pre-scale-performance/
│   │   ├── pre-scale-performance.css
│   │   ├── pre-scale-performance.html
│   │   └── pre-scale-performance.js
│   ├── prototype-inheritance/
│   │   ├── prototype-inheritance.html
│   │   └── prototype-inheritance.js
│   ├── rabbit-trap/
│   │   ├── 01/
│   │   │   ├── controller-01.js
│   │   │   ├── display-01.js
│   │   │   ├── engine-01.js
│   │   │   ├── game-01.js
│   │   │   └── main-01.js
│   │   ├── 02/
│   │   │   ├── controller-02.js
│   │   │   ├── display-02.js
│   │   │   ├── game-02.js
│   │   │   └── main-02.js
│   │   ├── 03/
│   │   │   ├── display-03.js
│   │   │   ├── game-03.js
│   │   │   └── main-03.js
│   │   ├── 04/
│   │   │   ├── display-04.js
│   │   │   └── game-04.js
│   │   ├── 05/
│   │   │   ├── display-05.js
│   │   │   ├── game-05.js
│   │   │   └── main-05.js
│   │   ├── 06/
│   │   │   ├── engine-06.js
│   │   │   ├── game-06.js
│   │   │   ├── main-06.js
│   │   │   ├── script.txt
│   │   │   ├── zone00.json
│   │   │   ├── zone01.json
│   │   │   ├── zone02.json
│   │   │   ├── zone03.json
│   │   │   └── zone04.json
│   │   ├── 07/
│   │   │   ├── game-07.js
│   │   │   ├── main-07.js
│   │   │   └── zone00.json
│   │   ├── rabbit-trap.css
│   │   └── rabbit-trap.html
│   ├── rectangle-collision/
│   │   └── rectangle-collision.html
│   ├── shoot/
│   │   └── shoot.html
│   ├── snake/
│   │   ├── snake.css
│   │   ├── snake.html
│   │   └── snake.js
│   ├── square-collision-response/
│   │   ├── response.css
│   │   ├── response.html
│   │   └── response.js
│   ├── starfield/
│   │   └── starfield.html
│   ├── stay-down/
│   │   ├── game-states/
│   │   │   ├── pause.js
│   │   │   ├── run.js
│   │   │   └── title.js
│   │   ├── initialize.js
│   │   ├── stay-down.html
│   │   ├── stay-down.js
│   │   ├── tools/
│   │   │   ├── controller.js
│   │   │   ├── engine.js
│   │   │   ├── loader.js
│   │   │   ├── state-manager.js
│   │   │   └── text.js
│   │   └── utilities/
│   │       ├── buffer.js
│   │       ├── collider.js
│   │       ├── frame.js
│   │       ├── item.js
│   │       ├── platform.js
│   │       ├── player.js
│   │       └── rectangle-2d.js
│   ├── tile-animation/
│   │   └── tile-animation.html
│   ├── tile-graphics/
│   │   ├── tile-graphics.css
│   │   ├── tile-graphics.html
│   │   └── tile-graphics.js
│   ├── tile-grid/
│   │   ├── tile-grid.css
│   │   ├── tile-grid.html
│   │   └── tile-grid.js
│   ├── tile-scroll/
│   │   └── tile-scroll.html
│   ├── tile-types/
│   │   ├── tile-types.css
│   │   ├── tile-types.html
│   │   └── tile-types.js
│   ├── tile-world/
│   │   ├── tile-world.css
│   │   ├── tile-world.html
│   │   └── tile-world.js
│   ├── top-down-tiles/
│   │   ├── top-down-tiles.css
│   │   ├── top-down-tiles.html
│   │   └── top-down-tiles.js
│   ├── touch-controller/
│   │   ├── touch-controller.css
│   │   ├── touch-controller.html
│   │   └── touch-controller.js
│   ├── vector-math/
│   │   └── vector-math.html
│   ├── walk-on-tiles/
│   │   ├── walk-on-tiles.css
│   │   ├── walk-on-tiles.html
│   │   └── walk-on-tiles.js
│   ├── web-app/
│   │   ├── manifest.json
│   │   ├── server.js
│   │   ├── ssl/
│   │   │   └── crt.pfx
│   │   ├── web-app.css
│   │   └── web-app.html
│   ├── wmw-basic/
│   │   └── basic.html
│   └── wmw-bouncing-balls/
│       └── bouncing-balls.html
├── data/
│   ├── logs.json
│   └── projects.json
├── index.css
├── index.html
├── index.js
├── library/
│   └── dom-kit.js
├── log.css
├── project.css
├── robots.txt
├── server.js
├── theme.css
└── tools/
    ├── log.js
    └── project.js
Download .txt
SYMBOL INDEX (114 symbols across 32 files)

FILE: content/better-tile-graphics/better-tile-graphics.js
  function calculateTileSourcePosition (line 44) | function calculateTileSourcePosition(tile_index, tile_sheet_columns) {
  function renderTiles (line 56) | function renderTiles() {
  function renderDisplay (line 84) | function renderDisplay() {
  function resize (line 91) | function resize(event) {

FILE: content/better-tile/better-tile.js
  function renderTiles (line 52) | function renderTiles() {
  function renderDisplay (line 80) | function renderDisplay() {
  function resize (line 87) | function resize(event) {

FILE: content/collision/collision.js
  method bottom (line 49) | get bottom() { return this.y + this.height; }
  method left (line 50) | get left() { return this.x; }
  method right (line 51) | get right() { return this.x + this.width; }
  method top (line 52) | get top() { return this.y; }

FILE: content/hit-the-wall/hit-the-wall.js
  method leftCollision (line 192) | leftCollision(object, column) {
  method rightCollision (line 219) | rightCollision(object, column) {
  method topCollision (line 240) | topCollision(object, row) {

FILE: content/inheritance/inheritance.js
  function Human (line 3) | function Human(name) {
  function Worker (line 9) | function Worker(name, job) {

FILE: content/ipo/components/main.js
  function update (line 12) | function update() {

FILE: content/multiple-inheritance/multiple-inheritance.js
  function Human (line 3) | function Human(name) {
  function Worker (line 19) | function Worker(job) {
  function Bob (line 35) | function Bob(job) {

FILE: content/objects-and-vars/objects.js
  function sayHello (line 22) | function sayHello() {

FILE: content/prototype-inheritance/prototype-inheritance.js
  function Human (line 3) | function Human(name) {
  function Worker (line 21) | function Worker(name, job) {

FILE: content/rabbit-trap/05/game-05.js
  method changeFrameSet (line 293) | changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {

FILE: content/rabbit-trap/06/game-06.js
  method changeFrameSet (line 62) | changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {
  method collideObject (line 277) | collideObject(object) {

FILE: content/rabbit-trap/07/game-07.js
  method changeFrameSet (line 55) | changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {

FILE: content/square-collision-response/response.js
  method centerX (line 53) | get centerX() { return this.x + this.width * 0.5; }
  method centerY (line 54) | get centerY() { return this.y + this.height * 0.5; }
  method bottom (line 56) | get bottom() { return this.y + this.height; }
  method left (line 57) | get left() { return this.x; }
  method right (line 58) | get right() { return this.x + this.width; }
  method top (line 59) | get top() { return this.y; }

FILE: content/stay-down/game-states/pause.js
  function activate (line 15) | function activate(message) {
  function deactivate (line 27) | function deactivate() {}
  function render (line 29) | function render() {}
  function update (line 31) | function update() {

FILE: content/stay-down/game-states/run.js
  function activate (line 53) | function activate(message) {
  function deactivate (line 62) | function deactivate() {}
  function render (line 64) | function render() {
  function reset (line 89) | function reset() {
  function update (line 103) | function update() {

FILE: content/stay-down/game-states/title.js
  function activate (line 20) | function activate(message) {
  function deactivate (line 35) | function deactivate() {}
  function render (line 37) | function render() {}
  function update (line 39) | function update() {

FILE: content/stay-down/initialize.js
  function resize (line 18) | function resize(event) {

FILE: content/stay-down/stay-down.js
  constant STAY_DOWN (line 1) | const STAY_DOWN = (() => ({

FILE: content/stay-down/tools/controller.js
  function activate (line 15) | function activate() {
  function getKey (line 22) | function getKey(name) { return keys[name].active }
  function keyDownUp (line 24) | function keyDownUp(event) { event.preventDefault();
  function setKey (line 40) | function setKey(name, active) { keys[name].active = active; }
  function trigger (line 42) | function trigger(input, state) { if (state !== input.state) input.active...

FILE: content/stay-down/tools/engine.js
  function cycle (line 13) | function cycle(time_stamp) {
  function start (line 38) | function start() {
  function stop (line 45) | function stop() {
  function setState (line 52) | function setState(state_) { state = state_; }

FILE: content/stay-down/tools/loader.js
  function loadImages (line 3) | function loadImages(urls, callback) {

FILE: content/stay-down/tools/state-manager.js
  function change (line 8) | function change(name, message) {

FILE: content/stay-down/tools/text.js
  function write (line 56) | function write(context, position_x, position_y, width, string, clear = f...

FILE: content/stay-down/utilities/buffer.js
  method create (line 3) | create(width, height, alpha = false, desynchronized = true) {
  method draw (line 15) | draw(b, image, x, y) {
  method drawFlippedX (line 21) | drawFlippedX(b, image, x, y, scale_x) {
  method resize (line 33) | resize(b, width, height, smoothing = false) {

FILE: content/stay-down/utilities/collider.js
  method collideRectangleWithRectangle (line 7) | collideRectangleWithRectangle(r1, r2) {
  method collideTop (line 15) | collideTop(rectangle, top) {
  method collidePlayerWithPlatform (line 27) | collidePlayerWithPlatform(player, platform) {
  method keepItemInBounds (line 37) | keepItemInBounds(item, left, right, top, bottom) {
  method keepPlayerInBounds (line 65) | keepPlayerInBounds(player, left, right) {

FILE: content/stay-down/utilities/frame.js
  method create (line 3) | create(x, y, width, height, offset_x = 0, offset_y = 0) {

FILE: content/stay-down/utilities/item.js
  method create (line 7) | create(x, y) {
  method reset (line 22) | reset(i, left, top, right, bottom) {
  method updatePosition (line 31) | updatePosition(i, x, y, friction) {

FILE: content/stay-down/utilities/platform.js
  method create (line 7) | create(x, y) {
  method moveUp (line 21) | moveUp(p) {
  method reset (line 31) | reset(p, y) {

FILE: content/stay-down/utilities/player.js
  method create (line 7) | create(x, y) {
  method ground (line 25) | ground(p, velocity_y = 0) {
  method jump (line 32) | jump(p) {
  method moveLeft (line 43) | moveLeft(p) {
  method moveRight (line 50) | moveRight(p) {
  method updatePosition (line 57) | updatePosition(p, gravity, friction) {

FILE: content/stay-down/utilities/rectangle-2d.js
  method create (line 3) | create(x, y, width, height) {
  method getBottom (line 17) | getBottom(r) { return r.y + r.height; }
  method getCenterX (line 18) | getCenterX(r) { return r.x + r.width * 0.5; }
  method getCenterY (line 19) | getCenterY(r) { return r.y + r.height * 0.5; }
  method getLeft (line 20) | getLeft(r) { return r.x; }
  method getRight (line 21) | getRight(r) { return r.x + r.width; }
  method getTop (line 22) | getTop(r) { return r.y; }
  method getOldBottom (line 24) | getOldBottom(r) { return r.old_y + r.height; }
  method getOldTop (line 25) | getOldTop(r) { return r.old_y; }
  method setBottom (line 27) | setBottom(r, y) { r.y = y - r.height; }
  method setLeft (line 28) | setLeft(r, x) { r.x = x; }
  method setRight (line 29) | setRight(r, x) { r.x = x - r.width; }
  method setTop (line 30) | setTop(r, y) { r.y = y; }
  method moveX (line 32) | moveX(r, x) { r.x += x; }
  method moveY (line 33) | moveY(r, y) { r.old_y = r.y; r.y += y; }

FILE: content/top-down-tiles/top-down-tiles.js
  method bottom (line 90) | get bottom()    { return this.y + this.height; }
  method oldBottom (line 91) | get oldBottom() { return this.old_y + this.height; }
  method left (line 92) | get left()      { return this.x; }
  method oldLeft (line 93) | get oldLeft()   { return this.old_x; }
  method right (line 94) | get right()     { return this.x + this.width; }
  method oldRight (line 95) | get oldRight()  { return this.old_x + this.width; }
  method top (line 96) | get top()       { return this.y; }
  method oldTop (line 97) | get oldTop()    { return this.old_y; }
  method leftCollision (line 174) | leftCollision(object, column) {
  method rightCollision (line 219) | rightCollision(object, column) {
  method bottomCollision (line 241) | bottomCollision(object, row) {
  method topCollision (line 259) | topCollision(object, row) {

FILE: index.js
  function clickOrTouchStart (line 11) | function clickOrTouchStart(event) {
  function emptyContainer (line 23) | function emptyContainer(container) {
  function fillContainer (line 29) | function fillContainer(container, elements) {
  function filter (line 35) | function filter(query) {
  function keyDown (line 56) | function keyDown(event) {
Condensed preview — 204 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (671K chars).
[
  {
    "path": "content/2018/minkowski-difference/minkowski-difference.html",
    "chars": 8751,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"description\" content = \"Get The Minkowski Difference between "
  },
  {
    "path": "content/animation/animation.css",
    "chars": 399,
    "preview": "/* Frank Poth 12/23/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/animation/animation.html",
    "chars": 528,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/animation/animation.js",
    "chars": 9339,
    "preview": "// Frank Poth 12/23/2017\r\n\r\n/* This example will show you how to do custom sprite animation in JavaScript.\r\nIt uses an A"
  },
  {
    "path": "content/animation-game-loop/animation.css",
    "chars": 337,
    "preview": "/* Frank Poth 08/12/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml, body {\r\n\r\n  heig"
  },
  {
    "path": "content/animation-game-loop/animation.html",
    "chars": 384,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>Animati"
  },
  {
    "path": "content/animation-game-loop/animation.js",
    "chars": 834,
    "preview": "// Frank Poth 08/12/2017\r\n\r\nvar context, rectangle, loop;\r\n\r\ncontext = document.querySelector(\"canvas\").getContext(\"2d\")"
  },
  {
    "path": "content/better-tile/better-tile.html",
    "chars": 1026,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta charset = \"utf-8\">\r\n    <meta name = \"author\"       content = \"Frank "
  },
  {
    "path": "content/better-tile/better-tile.js",
    "chars": 4637,
    "preview": "(() => {\r\n\r\n  // The display canvas' context. Draw the tile buffer here.\r\n  const DISPLAY = document.querySelector('canv"
  },
  {
    "path": "content/better-tile-graphics/better-tile-graphics.html",
    "chars": 1132,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta charset = \"utf-8\">\r\n    <meta name = \"author\"       content = \"Frank "
  },
  {
    "path": "content/better-tile-graphics/better-tile-graphics.js",
    "chars": 5370,
    "preview": "(() => {\r\n\r\n  // The display canvas' context. Draw the tile buffer here. It's important not to desynchronize when using "
  },
  {
    "path": "content/blit/blit.css",
    "chars": 1462,
    "preview": "/* Frank Poth 12/26/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n  user-select:none;\r\n\r\n}\r\n\r\nh"
  },
  {
    "path": "content/blit/blit.html",
    "chars": 1144,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/blit/blit.js",
    "chars": 6003,
    "preview": "// Frank Poth 12/27/2017\r\n\r\n/* This program tests drawImage and putImageData efficiency. Basically, I run these\r\nfunctio"
  },
  {
    "path": "content/bouncing-polygons/bouncing-polygons.html",
    "chars": 17532,
    "preview": "<!DOCTYPE HTML>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"description\" content = \"Get the bounding rectangle of an arbi"
  },
  {
    "path": "content/calculator/calculator.css",
    "chars": 4336,
    "preview": "/* Frank Poth 12/29/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n  user-select:none;\r\n\r\n}\r\n\r\nh"
  },
  {
    "path": "content/calculator/calculator.html",
    "chars": 3238,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"initial-scale=1,user-scalable=no,width=d"
  },
  {
    "path": "content/calculator/calculator.js",
    "chars": 8359,
    "preview": "// Frank Poth 12/29/2017\r\n\r\n(function() { \"use strict\";\r\n\r\n  const PI = Math.PI;\r\n  const pow = Math.pow;\r\n  const sqrt "
  },
  {
    "path": "content/canvas/canvas.css",
    "chars": 377,
    "preview": "/* Frank Poth 08/04/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nbody, html {\r\n\r\n  widt"
  },
  {
    "path": "content/canvas/canvas.html",
    "chars": 400,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <link href = \""
  },
  {
    "path": "content/canvas/canvas.js",
    "chars": 930,
    "preview": "// Frank Poth 08/04/2017\r\n\r\nvar display = document.getElementById(\"display\").getContext(\"2d\");\r\n\r\ndisplay.canvas.height "
  },
  {
    "path": "content/circle-collision-detection/circle-collision-detection.html",
    "chars": 9195,
    "preview": "<!DOCTYPE html>\r\n\r\n<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision\r\ndetection. To d"
  },
  {
    "path": "content/circle-collision-response/circle-collision-response.html",
    "chars": 9107,
    "preview": "<!DOCTYPE html>\r\n\r\n<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision\r\nresponse. -->\r\n"
  },
  {
    "path": "content/collision/collision.css",
    "chars": 328,
    "preview": "/* Frank Poth 08/29/2017 */\r\n\r\n* {\r\n\r\n  box-sizing: border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml, body {\r\n\r\n  hei"
  },
  {
    "path": "content/collision/collision.html",
    "chars": 393,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Collision</title>\r\n    <meta name = \"viewport\" content = \"width=devi"
  },
  {
    "path": "content/collision/collision.js",
    "chars": 2929,
    "preview": "// Frank Poth 08/29/2017\r\n\r\nvar context, controller, Rectangle, red, white, loop, resize;\r\n\r\ncontext = document.querySel"
  },
  {
    "path": "content/control/control.css",
    "chars": 337,
    "preview": "/* Frank Poth 08/13/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml, body {\r\n\r\n  heig"
  },
  {
    "path": "content/control/control.html",
    "chars": 444,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>PoP Vlo"
  },
  {
    "path": "content/control/control.js",
    "chars": 2397,
    "preview": "// Frank Poth 08/13/2017\r\n\r\nvar context, controller, rectangle, loop;\r\n\r\ncontext = document.querySelector(\"canvas\").getC"
  },
  {
    "path": "content/cube/cube.html",
    "chars": 4990,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta charset = \"utf-8\">\r\n    <meta name = \"viewport\" content = \"width=devi"
  },
  {
    "path": "content/dino/dino.css",
    "chars": 420,
    "preview": "/* Frank Poth 12/24/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n  user-select:none;\r\n\r\n}\r\n\r\nh"
  },
  {
    "path": "content/dino/dino.html",
    "chars": 513,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/dino/dino.js",
    "chars": 24641,
    "preview": "// Frank Poth 12/24/2017\r\n\r\n/* This example has a lot packed into it. It has a scrolling tile based background.\r\nThe rig"
  },
  {
    "path": "content/dominiques-doors/area0.json",
    "chars": 198,
    "preview": "{\r\n\r\n  \"message\":\"~The Tiny Closet~\",\r\n  \"background_color\":\"#052623\",\r\n  \"floor\":62,\r\n  \"height\":64,\r\n  \"width\":128,\r\n\r"
  },
  {
    "path": "content/dominiques-doors/area1.json",
    "chars": 300,
    "preview": "{\r\n\r\n  \"message\":\"~The Main Lobby~\",\r\n  \"background_color\":\"#003333\",\r\n  \"floor\":120,\r\n  \"height\":128,\r\n  \"width\":256,\r\n"
  },
  {
    "path": "content/dominiques-doors/area2.json",
    "chars": 208,
    "preview": "{\r\n\r\n  \"message\":\"~The Giant Pointless Room~\",\r\n  \"background_color\":\"#006666\",\r\n  \"floor\":250,\r\n  \"height\":256,\r\n  \"wid"
  },
  {
    "path": "content/dominiques-doors/area3.json",
    "chars": 197,
    "preview": "{\r\n\r\n  \"message\":\"~The Passageway~\",\r\n  \"background_color\":\"#148977\",\r\n  \"floor\":60,\r\n  \"height\":64,\r\n  \"width\":200,\r\n\r\n"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.css",
    "chars": 445,
    "preview": "/* Frank Poth 01/14/2017 */\r\n\r\n* {\r\n\r\n  margin:0;\r\n  padding:0;\r\n  box-sizing:border-box;\r\n\r\n}\r\n\r\nhtml { height:100%; wi"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.html",
    "chars": 552,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"dominiques-doors.css\" rel = \"stylesheet\" type = \"text/css\">\r\n"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.js",
    "chars": 13092,
    "preview": "// Frank Poth 01/14/2017\r\n\r\n/* By studying this example program, you can learn how to load json levels, how\r\nto animate "
  },
  {
    "path": "content/elements/elements.html",
    "chars": 446,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title></title>\r\n\r\n  </head>\r\n\r\n  <body style = \"background-color:#404040; "
  },
  {
    "path": "content/elements/elements.js",
    "chars": 308,
    "preview": "// Frank Poth 08/03/2017\r\n\r\n\r\nvar input = document.getElementById(\"input\");\r\nvar output = document.getElementById(\"outpu"
  },
  {
    "path": "content/gjk/gjk.html",
    "chars": 8661,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/hello-world/hello.html",
    "chars": 300,
    "preview": "<DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Hello, world!</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>Hello, worl"
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.css",
    "chars": 488,
    "preview": "/* Frank Poth 11/16/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.html",
    "chars": 655,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"hit-the-wall.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    "
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.js",
    "chars": 14589,
    "preview": "// Frank Poth 11/16/2017\r\n\r\n(function() { \"use strict\"\r\n\r\n  // the three main components of the example\r\n  var controlle"
  },
  {
    "path": "content/hitbox/hitbox.html",
    "chars": 12312,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n        \r\n    <meta name = \"description\" content = \"Interactive example of player"
  },
  {
    "path": "content/https-server/index.css",
    "chars": 336,
    "preview": "/* Frank Poth 10/16/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/https-server/index.html",
    "chars": 472,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>HTTPS Server!</title>\r\n\r\n    <meta name = \"viewport\" content = \"widt"
  },
  {
    "path": "content/https-server/server.js",
    "chars": 2579,
    "preview": "// Frank Poth 10/16/2017\r\n\r\n(function() {\r\n\r\n  var fs, https, mimetypes, options, path, server;\r\n\r\n  fs = require(\"fs\");"
  },
  {
    "path": "content/https-server/ssl/crt.cnf",
    "chars": 952,
    "preview": "[req]\r\ndays                   = 180\r\nserial                 = 1\r\ndistinguished_name     = req_distinguished_name\r\nx509_e"
  },
  {
    "path": "content/https-server/ssl/csr.cnf",
    "chars": 659,
    "preview": "[req]\r\n\r\ndays                   = 180\r\ndistinguished_name     = req_distinguished_name\r\nreq_extensions         = v3_req\r"
  },
  {
    "path": "content/https-server/ssl/make-crt.sh",
    "chars": 816,
    "preview": "#!/bin/bash\r\n \r\necho \"\"\r\necho \"Generate a self signed certificate:\"\r\necho \"Enter certificate name (example: crt.crt):\"\r\n"
  },
  {
    "path": "content/https-server/ssl/make-csr.sh",
    "chars": 555,
    "preview": "#!/bin/bash\r\n \r\necho \"\"\r\necho \"Generate a Certificate Signing Request (CSR) file:\"\r\necho \"Enter CSR name (example: csr.c"
  },
  {
    "path": "content/https-server/ssl/make-key.sh",
    "chars": 399,
    "preview": "#!/bin/bash\r\n \r\necho \"\"\r\necho \"Generate a private Key file in PEM format:\"\r\necho \"Enter key name (example: key.key):\"\r\nr"
  },
  {
    "path": "content/https-server/ssl/make-pfx.sh",
    "chars": 558,
    "preview": "#!/bin/bash\r\n\r\necho \"\"\r\necho \"Generate a Public Key Cryptography Standards # 12 ( PKCS#12 ) file:\"\r\necho \"Enter certific"
  },
  {
    "path": "content/indexed-db/index.css",
    "chars": 1055,
    "preview": "/* Frank Poth 11/08/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/indexed-db/index.html",
    "chars": 5698,
    "preview": "<!DOCTYPE html>\r\n\r\n<!-- Here's a great resource: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_In"
  },
  {
    "path": "content/inheritance/inheritance.html",
    "chars": 258,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>Inher"
  },
  {
    "path": "content/inheritance/inheritance.js",
    "chars": 328,
    "preview": "// Frank Poth 08/02/2017\r\n\r\nfunction Human(name) {\r\n\r\n  this.name = name;\r\n\r\n}\r\n\r\nfunction Worker(name, job) {\r\n\r\n  Huma"
  },
  {
    "path": "content/inventory/inventory.html",
    "chars": 9378,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <style>\r\n\r\n "
  },
  {
    "path": "content/ipo/components/input.js",
    "chars": 254,
    "preview": "// Frank Poth 03/06/2018\r\n\r\n/* The input class handles everything to do with user input. */\r\n\r\nconst Input = function(up"
  },
  {
    "path": "content/ipo/components/main.js",
    "chars": 808,
    "preview": "// Frank Poth 03/07/2018\r\n\r\n/* This is the main file where all of the different components of the application\r\ncome toge"
  },
  {
    "path": "content/ipo/components/output.js",
    "chars": 330,
    "preview": "// Frank Poth 03/06/2018\r\n\r\n/* The output class handles everything to do with displaying graphics. */\r\n\r\nconst Output = "
  },
  {
    "path": "content/ipo/components/processor.js",
    "chars": 305,
    "preview": "// Frank Poth 03/06/2018\r\n\r\n/* The processor class handles the application logic. */\r\n\r\nconst Processor = function() {\r\n"
  },
  {
    "path": "content/ipo/ipo.html",
    "chars": 1932,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>IPO</"
  },
  {
    "path": "content/json/json.html",
    "chars": 1072,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/json/json.js",
    "chars": 1451,
    "preview": "// Frank Poth 01/14/2017\r\n\r\n/* This program simply loads a json file and gets the data out of it. */\r\n(function() { \"use"
  },
  {
    "path": "content/json/json.json",
    "chars": 189,
    "preview": "{\r\n\r\n  \"array\":[\"apple\", \"sandwich\", 2, \"a dog\"],\r\n  \"number\":4,\r\n  \"string\":\"Hello, I'm a string!\",\r\n\r\n  \"object\": {\r\n\r"
  },
  {
    "path": "content/load-image/load-image.css",
    "chars": 399,
    "preview": "/* Frank Poth 12/22/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/load-image/load-image.html",
    "chars": 899,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/load-image/load-image.js",
    "chars": 7206,
    "preview": "// Frank Poth 12/22/2017\r\n\r\n(function() { \"use strict\";\r\n\r\n  /* load0 is the simplest way to get an image, but it's lazy"
  },
  {
    "path": "content/multiple-inheritance/multiple-inheritance.html",
    "chars": 267,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>Inher"
  },
  {
    "path": "content/multiple-inheritance/multiple-inheritance.js",
    "chars": 640,
    "preview": "// Frank Poth 08/03/2017\r\n\r\nfunction Human(name) {\r\n\r\n  this.name = name;\r\n\r\n}\r\n\r\nHuman.prototype = {\r\n\r\n  talk:function"
  },
  {
    "path": "content/objects-and-vars/objects.html",
    "chars": 196,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Objects And Vars</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <script src"
  },
  {
    "path": "content/objects-and-vars/objects.js",
    "chars": 366,
    "preview": "\r\n\r\nvar time = \"3:43\";\r\n\r\nvar rectangle = {\r\n\r\n  x:0,\r\n  y:10,\r\n  width:100,\r\n  height:200,\r\n\r\n  print:function(what_to_"
  },
  {
    "path": "content/offline-web-app/manifest.json",
    "chars": 491,
    "preview": "{\r\n\r\n  \"author\": \"PoP Vlog\",\r\n  \"background_color\": \"#000000\",\r\n  \"description\": \"A simple Progressive Web Application\","
  },
  {
    "path": "content/offline-web-app/server.js",
    "chars": 2614,
    "preview": "// Frank Poth 10/16/2017\r\n\r\n(function() {\r\n\r\n  var fs, https, mimetypes, options, path, server;\r\n\r\n  fs = require(\"fs\");"
  },
  {
    "path": "content/offline-web-app/web-app-service.js",
    "chars": 944,
    "preview": "// Frank Poth 10/25/2017\r\n// here's a great resource on service workers: https://developer.mozilla.org/en-US/docs/Web/AP"
  },
  {
    "path": "content/offline-web-app/web-app.css",
    "chars": 480,
    "preview": "/* Frank Poth 10/17/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/offline-web-app/web-app.html",
    "chars": 1377,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta charset = \"utf-8\">\r\n    <title>Web App</title>\r\n    <meta name = \"des"
  },
  {
    "path": "content/pagination/article1.txt",
    "chars": 447,
    "preview": "1. Pagination is a verb. To paginate content one must break that content into smaller\r\ngroups. For instance, if I had a "
  },
  {
    "path": "content/pagination/article2.txt",
    "chars": 686,
    "preview": "2. Pagination is used in many applications, perhaps without you even knowing it. If\r\nyou have used a search engine recen"
  },
  {
    "path": "content/pagination/article3.txt",
    "chars": 853,
    "preview": "3. The most common use case of pagination is to organize search results requested from\r\na database. Good examples of thi"
  },
  {
    "path": "content/pagination/article4.txt",
    "chars": 750,
    "preview": "4. If you are planning to build a static website and want a simple paginator\r\nto help your users view photos or links, t"
  },
  {
    "path": "content/pagination/article5.txt",
    "chars": 603,
    "preview": "5. Your static site can greatly benefit from AJAX and the XMLHttpRequest. This paginator\r\nrelies entirely on XMLHttpRequ"
  },
  {
    "path": "content/pagination/pagination.css",
    "chars": 1451,
    "preview": "/* Frank Poth 01/27/2017 */\r\n\r\n* {\r\n\r\n  margin:0;\r\n  padding:0;\r\n  box-sizing:border-box;\r\n\r\n}\r\n\r\nhtml { height:100%; wi"
  },
  {
    "path": "content/pagination/pagination.html",
    "chars": 1280,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <!-- The css for the paginator are in the main css file for this project. -"
  },
  {
    "path": "content/pagination/paginator.js",
    "chars": 5976,
    "preview": "// Frank Poth 01/27/2018\r\n\r\n/* This paginator is designed for static websites, making it quite rare. People\r\ndon't usual"
  },
  {
    "path": "content/particle-pool/particle-pool.html",
    "chars": 5414,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/platform/platform.html",
    "chars": 7010,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n\t<head>\r\n\r\n\t\t<title>Platforms</title>\r\n\r\n\t\t<style>\r\n\r\n\t\t\t* { margin:0; padding:0; }\r\n\r\n\t\t\th"
  },
  {
    "path": "content/platformer-ai/platformer-ai.html",
    "chars": 7809,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n\t<head>\r\n\r\n\t\t<title>Platformer AI</title>\r\n\r\n\t\t<style>\r\n\r\n\t\t\t* { margin:0; padding:0; }\r\n\r\n"
  },
  {
    "path": "content/polygon/polygon.html",
    "chars": 6079,
    "preview": "<!DOCTYPE HTML>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"description\" content = \"An interactive, open source polygon e"
  },
  {
    "path": "content/polygon-rotation/polygon-rotation.html",
    "chars": 6254,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.css",
    "chars": 945,
    "preview": "/* Frank Poth 02/12/2018 */\r\n\r\n* {\r\n\r\n  margin:0;\r\n  padding:0;\r\n  box-sizing:border-box;\r\n\r\n}\r\n\r\nhtml { height:100%; wi"
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.html",
    "chars": 1220,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"pre-scale-performance.css\" rel = \"stylesheet\" type = \"text/cs"
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.js",
    "chars": 10872,
    "preview": "// Frank Poth 02/12/2018\r\n\r\n(function() { \"use strict\";\r\n\r\n  const Ball = function(x, y) {\r\n\r\n    this.frame_index = Mat"
  },
  {
    "path": "content/prototype-inheritance/prototype-inheritance.html",
    "chars": 268,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>Inher"
  },
  {
    "path": "content/prototype-inheritance/prototype-inheritance.js",
    "chars": 674,
    "preview": "// Frank Poth 08/02/2017\r\n\r\nfunction Human(name) {\r\n\r\n  this.name = name;\r\n\r\n}\r\n\r\nHuman.prototype = {\r\n\r\n  constructor:H"
  },
  {
    "path": "content/rabbit-trap/01/controller-01.js",
    "chars": 1248,
    "preview": "// Frank Poth 02/28/2018\r\n\r\n/* In this example, the controller only alerts the user whenever they press a key,\r\nbut it a"
  },
  {
    "path": "content/rabbit-trap/01/display-01.js",
    "chars": 1111,
    "preview": "// Frank Poth 02/28/2018\r\n\r\n/* This Display class contains the screen resize event handler and also handles\r\ndrawing col"
  },
  {
    "path": "content/rabbit-trap/01/engine-01.js",
    "chars": 3217,
    "preview": "// Frank Poth 02/28/2018\r\n\r\n/* This is a fixed time step game loop. It can be used for any game and will ensure\r\nthat ga"
  },
  {
    "path": "content/rabbit-trap/01/game-01.js",
    "chars": 917,
    "preview": "// Frank Poth 02/28/2018\r\n\r\n/* To keep this example from looking too boring, I made the game logic gradually\r\nchange som"
  },
  {
    "path": "content/rabbit-trap/01/main-01.js",
    "chars": 2333,
    "preview": "// Frank Poth 02/28/2018\r\n\r\n/* This is the basic setup or \"skeleton\" of my program. It has three main parts:\r\nthe contro"
  },
  {
    "path": "content/rabbit-trap/02/controller-02.js",
    "chars": 908,
    "preview": "// Frank Poth 03/09/2018\r\n\r\n/* The keyDownUp handler was moved to the main file. */\r\n\r\nconst Controller = function() {\r\n"
  },
  {
    "path": "content/rabbit-trap/02/display-02.js",
    "chars": 1353,
    "preview": "// Frank Poth 03/09/2018\r\n\r\n/* This class hasn't changed much since part 1. All it does now is resize the canvas\r\na bit "
  },
  {
    "path": "content/rabbit-trap/02/game-02.js",
    "chars": 2379,
    "preview": "// Frank Poth 03/09/2018\r\n\r\n/* The Game class has been updated with a new Player class and given a new world\r\nobject tha"
  },
  {
    "path": "content/rabbit-trap/02/main-02.js",
    "chars": 3501,
    "preview": "// Frank Poth 03/09/2018\r\n\r\n/* In part 2, I added a player character to the screen. I created world\r\nboundaries with col"
  },
  {
    "path": "content/rabbit-trap/03/display-03.js",
    "chars": 2511,
    "preview": "// Frank Poth 03/23/2018\r\n\r\n/* I moved some generic functions to the Display.prototype.\r\nI created the Display.TileSheet"
  },
  {
    "path": "content/rabbit-trap/03/game-03.js",
    "chars": 3493,
    "preview": "// Frank Poth 03/23/2018\r\n\r\n/* I moved the world object into its own class and I made the Player class a class\r\ninside o"
  },
  {
    "path": "content/rabbit-trap/03/main-03.js",
    "chars": 1807,
    "preview": "// Frank Poth 03/23/2017\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n      //////////////"
  },
  {
    "path": "content/rabbit-trap/04/display-04.js",
    "chars": 2718,
    "preview": "// Frank Poth 03/23/2018\r\n\r\n/* I changed a few small things since part 3. First, I got rid of my tile value\r\noffset when"
  },
  {
    "path": "content/rabbit-trap/04/game-04.js",
    "chars": 14489,
    "preview": "// Frank Poth 03/28/2018\r\n\r\n/* In part 4 I added collision detection and response for the tile map. I also\r\nfixed the ti"
  },
  {
    "path": "content/rabbit-trap/05/display-05.js",
    "chars": 2030,
    "preview": "// Frank Poth 04/03/2018\r\n\r\n/* Changes:\r\n1. Removed the TileSheet class from part 3 and added the Game.World.TileSet cla"
  },
  {
    "path": "content/rabbit-trap/05/game-05.js",
    "chars": 17670,
    "preview": "// Frank Poth 04/03/2018\r\n\r\n/* Changes since part 4:\r\n1. Added the Game.World.TileSet & Game.World.TileSet.Frame classes"
  },
  {
    "path": "content/rabbit-trap/05/main-05.js",
    "chars": 4069,
    "preview": "// Frank Poth 04/03/2018\r\n\r\n/* Changes:\r\n\r\n1. I added an AssetsManager class which will eventually store all my graphics"
  },
  {
    "path": "content/rabbit-trap/06/engine-06.js",
    "chars": 2231,
    "preview": "// Frank Poth 04/09/2018\r\n\r\n/* I made a minor mistake the first time I wrote this engine class. Instead of calling\r\nwind"
  },
  {
    "path": "content/rabbit-trap/06/game-06.js",
    "chars": 18077,
    "preview": "// Frank Poth 04/06/2018\r\n\r\n/* Changes since part 5:\r\n\r\n  1. Simplified Class constructors by removing multiple prefixes"
  },
  {
    "path": "content/rabbit-trap/06/main-06.js",
    "chars": 4941,
    "preview": "// Frank Poth 04/06/2018\r\n\r\n/* Changes:\r\n\r\n  1. The update function now check on every frame for game.world.door. If a d"
  },
  {
    "path": "content/rabbit-trap/06/script.txt",
    "chars": 1083,
    "preview": "INTRO\r\n\r\nHey, guys, my name is Frank and THIS is the Poth on Programming Video Log Part 6\r\nof how to make a tile based p"
  },
  {
    "path": "content/rabbit-trap/06/zone00.json",
    "chars": 778,
    "preview": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":192, \"y\":64, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"01\", \"destination_x\":0,"
  },
  {
    "path": "content/rabbit-trap/06/zone01.json",
    "chars": 903,
    "preview": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":64, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"00\", \"destination_x\":19"
  },
  {
    "path": "content/rabbit-trap/06/zone02.json",
    "chars": 905,
    "preview": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":16, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"01\", \"destination_x\":19"
  },
  {
    "path": "content/rabbit-trap/06/zone03.json",
    "chars": 901,
    "preview": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":32, \"y\":-16, \"width\":80, \"height\":16,\r\n      \"destination_zone\":\"02\", \"destination_x\":-1"
  },
  {
    "path": "content/rabbit-trap/06/zone04.json",
    "chars": 779,
    "preview": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":80, \"width\":16, \"height\":64,\r\n      \"destination_zone\":\"03\", \"destination_x\":19"
  },
  {
    "path": "content/rabbit-trap/07/game-07.js",
    "chars": 19476,
    "preview": "// Frank Poth 04/18/2018\r\n\r\n/* Changes since part 6:\r\n\r\n  1. Added the carrots array to the zone file.\r\n  2. Moved the c"
  },
  {
    "path": "content/rabbit-trap/07/main-07.js",
    "chars": 5078,
    "preview": "// Frank Poth 04/18/2018\r\n\r\n/* Changes:\r\n\r\n  1. Added the drawing calls for drawing the grass and carrots in render.\r\n  "
  },
  {
    "path": "content/rabbit-trap/07/zone00.json",
    "chars": 819,
    "preview": "{\r\n\r\n  \"carrots\":[[1, 2], [4, 2], [6, 2], [10, 2], [3, 4], [8, 4], [6, 5], [10, 5], [1, 6], [4, 6]],\r\n  \"grass\"  :[[2, 7"
  },
  {
    "path": "content/rabbit-trap/rabbit-trap.css",
    "chars": 819,
    "preview": "/* Frank Poth 02/28/2018 */\r\n\r\n* { box-sizing:border-box; margin:0; padding:0; }\r\n\r\nhtml { height:100%; width:100%; }\r\n\r"
  },
  {
    "path": "content/rabbit-trap/rabbit-trap.html",
    "chars": 2942,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <!-- Added this meta tag 04/07/2018 -->\r\n    <meta name = \"viewport\" conten"
  },
  {
    "path": "content/rectangle-collision/rectangle-collision.html",
    "chars": 5026,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <style>\r\n\r\n "
  },
  {
    "path": "content/shoot/shoot.html",
    "chars": 4886,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n\t<head>\r\n\r\n\t\t<meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n\t\t<title>Shoot</tit"
  },
  {
    "path": "content/snake/snake.css",
    "chars": 586,
    "preview": "/* Frank Poth 11/16/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/snake/snake.html",
    "chars": 686,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"snake.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    <meta n"
  },
  {
    "path": "content/snake/snake.js",
    "chars": 12874,
    "preview": "// Frank Poth 11/20/2017\r\n\r\n(function() {\r\n\r\n  /* I split the logic into three separate parts for organizational purpose"
  },
  {
    "path": "content/square-collision-response/response.css",
    "chars": 363,
    "preview": "/* Frank Poth 08/30/2017 */\r\n\r\n* {\r\n\r\n  box-sizing: border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml, body {\r\n\r\n  hei"
  },
  {
    "path": "content/square-collision-response/response.html",
    "chars": 399,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Response</title>\r\n    <meta name = \"viewport\" content = \"width=devic"
  },
  {
    "path": "content/square-collision-response/response.js",
    "chars": 5563,
    "preview": "// Frank Poth 08/30/2017\r\n\r\n// drawing context, controller object, Rectangle class,\r\n// the red and white rectangle obje"
  },
  {
    "path": "content/starfield/starfield.html",
    "chars": 2277,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>StarFie"
  },
  {
    "path": "content/stay-down/game-states/pause.js",
    "chars": 1087,
    "preview": "STAY_DOWN.initializers.pauseState = () => {\n\n  // buffers\n  const display_buffer = STAY_DOWN.buffers.display;\n  const te"
  },
  {
    "path": "content/stay-down/game-states/run.js",
    "chars": 5001,
    "preview": "STAY_DOWN.initializers.runState = () => {\n\n  // buffers\n  const background_buffer = STAY_DOWN.buffers.background;\n  cons"
  },
  {
    "path": "content/stay-down/game-states/title.js",
    "chars": 1411,
    "preview": "STAY_DOWN.initializers.titleState = () => {\r\n\r\n  // buffers\r\n  const background_buffer = STAY_DOWN.buffers.background;\r\n"
  },
  {
    "path": "content/stay-down/initialize.js",
    "chars": 3226,
    "preview": "(() => {\r\n\r\n  const BUFFERS = STAY_DOWN.buffers;\r\n  const IMAGES = STAY_DOWN.images;\r\n  const INITIALIZERS = STAY_DOWN.i"
  },
  {
    "path": "content/stay-down/stay-down.html",
    "chars": 1404,
    "preview": "<!DOCTYPE html>\n\n<html>\n\n  <head>\n\n    <meta charset = 'utf-8'>\n    <meta name = 'viewport' content = 'width=device-widt"
  },
  {
    "path": "content/stay-down/stay-down.js",
    "chars": 122,
    "preview": "const STAY_DOWN = (() => ({\n\n  buffers:{},\n  images:{},\n  initializers:{},\n  states:{},\n  tools:{},\n  utilities:{}\n\n}))("
  },
  {
    "path": "content/stay-down/tools/controller.js",
    "chars": 982,
    "preview": "STAY_DOWN.tools.controller = (() => {\n\n  const Input = () => ({ active:false, state:false });\n\n  const keys = {\n\n    'le"
  },
  {
    "path": "content/stay-down/tools/engine.js",
    "chars": 924,
    "preview": "STAY_DOWN.tools.engine = (() => {\n\n  var running = false;\n\n  var raf_handle;\n\n  var accumulated_time = 0;\n  var current_"
  },
  {
    "path": "content/stay-down/tools/loader.js",
    "chars": 651,
    "preview": "STAY_DOWN.tools.loader = (() => {\n\n  function loadImages(urls, callback) {\n\n    var images = [];\n    var counter = urls."
  },
  {
    "path": "content/stay-down/tools/state-manager.js",
    "chars": 390,
    "preview": "STAY_DOWN.initializers.stateManagerTool = () => {\r\n\r\n  const ENGINE = STAY_DOWN.tools.engine;\r\n  const STATES = STAY_DOW"
  },
  {
    "path": "content/stay-down/tools/text.js",
    "chars": 2501,
    "preview": "STAY_DOWN.initializers.textTool = () => {\n\n  const image = STAY_DOWN.images.text;\n  const Frame = STAY_DOWN.utilities.Fr"
  },
  {
    "path": "content/stay-down/utilities/buffer.js",
    "chars": 895,
    "preview": "STAY_DOWN.utilities.Buffer = {\r\n\r\n  create(width, height, alpha = false, desynchronized = true) {\r\n\r\n    const buffer = "
  },
  {
    "path": "content/stay-down/utilities/collider.js",
    "chars": 1943,
    "preview": "STAY_DOWN.initializers.colliderUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOW"
  },
  {
    "path": "content/stay-down/utilities/frame.js",
    "chars": 170,
    "preview": "STAY_DOWN.utilities.Frame = {\r\n  \r\n  create(x, y, width, height, offset_x = 0, offset_y = 0) {\r\n    \r\n    return { x, y,"
  },
  {
    "path": "content/stay-down/utilities/item.js",
    "chars": 1395,
    "preview": "STAY_DOWN.initializers.itemUtility = () => {\n\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\n  \n  STAY_DOWN.util"
  },
  {
    "path": "content/stay-down/utilities/platform.js",
    "chars": 749,
    "preview": "STAY_DOWN.initializers.platformUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOW"
  },
  {
    "path": "content/stay-down/utilities/player.js",
    "chars": 1152,
    "preview": "STAY_DOWN.initializers.playerUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOWN."
  },
  {
    "path": "content/stay-down/utilities/rectangle-2d.js",
    "chars": 770,
    "preview": "STAY_DOWN.utilities.Rectangle2D = {\r\n\r\n  create(x, y, width, height) {\r\n    \r\n    return {\r\n  \r\n      height:height,\r\n  "
  },
  {
    "path": "content/tile-animation/tile-animation.html",
    "chars": 4221,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>Tile An"
  },
  {
    "path": "content/tile-graphics/tile-graphics.css",
    "chars": 449,
    "preview": "/* Frank Poth 02/09/2018 */\r\n\r\n* {\r\n\r\n  margin:0;\r\n  padding:0;\r\n  box-sizing:border-box;\r\n\r\n}\r\n\r\nhtml { height:100%; wi"
  },
  {
    "path": "content/tile-graphics/tile-graphics.html",
    "chars": 714,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"tile-graphics.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n "
  },
  {
    "path": "content/tile-graphics/tile-graphics.js",
    "chars": 4937,
    "preview": "// Frank Poth 02/09/2018\r\n\r\n/* This example simply blits tile graphics to a buffer canvas according to a 1d\r\ntile map. T"
  },
  {
    "path": "content/tile-grid/tile-grid.css",
    "chars": 388,
    "preview": "/* Frank Poth 09/29/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/tile-grid/tile-grid.html",
    "chars": 438,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Tile Grid</title>\r\n\r\n    <meta name = \"viewport\" content = \"width=de"
  },
  {
    "path": "content/tile-grid/tile-grid.js",
    "chars": 2987,
    "preview": "// Frank Poth 09/29/2017\r\n\r\n(function() {\r\n\r\n  var buffer, context, controller, drawMap, loop, map, output, size;\r\n\r\n  b"
  },
  {
    "path": "content/tile-scroll/tile-scroll.html",
    "chars": 7998,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta charset = \"utf-8\">\r\n    <meta name = \"viewport\" content = \"width=devi"
  },
  {
    "path": "content/tile-types/tile-types.css",
    "chars": 627,
    "preview": "/* Frank Poth 12/05/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/tile-types/tile-types.html",
    "chars": 939,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"tile-types.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    <m"
  },
  {
    "path": "content/tile-types/tile-types.js",
    "chars": 16717,
    "preview": "// Frank Poth 12/05/2017\r\n\r\n(function() { \"use strict\";\r\n\r\n  const TILE_SIZE = 16;\r\n\r\n      /////////////////\r\n    //// "
  },
  {
    "path": "content/tile-world/tile-world.css",
    "chars": 428,
    "preview": "/* Frank Poth 07/16/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/tile-world/tile-world.html",
    "chars": 405,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>PoP Vlog - Tile World</title>\r\n\r\n    <meta name = \"viewport\" content"
  },
  {
    "path": "content/tile-world/tile-world.js",
    "chars": 1624,
    "preview": "// Frank Poth 09/27/2017\r\n\r\n(function() {\r\n\r\n  var buffer, context, drawMap, map, size;\r\n\r\n  buffer = document.createEle"
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.css",
    "chars": 488,
    "preview": "/* Frank Poth 11/16/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.html",
    "chars": 655,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"veiwport\" content = \"width=device-width\">\r\n    <meta name = \""
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.js",
    "chars": 17942,
    "preview": "// Frank Poth 11/20/2017\r\n// just a note, my \"j\" key is very unresponsive. If there are Js missing, that's why.\r\n\r\n(func"
  },
  {
    "path": "content/touch-controller/touch-controller.css",
    "chars": 412,
    "preview": "/* Frank Poth 10/03/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/touch-controller/touch-controller.html",
    "chars": 478,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>touch controller</title>\r\n\r\n    <meta name = \"viewport\" content = \"w"
  },
  {
    "path": "content/touch-controller/touch-controller.js",
    "chars": 7879,
    "preview": "// Frank Poth 10/03/2017\r\n\r\n(function() {\r\n\r\n  var Button, controller, display, game;\r\n\r\n  // basically a rectangle, but"
  },
  {
    "path": "content/vector-math/vector-math.html",
    "chars": 14406,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"description\" content = \"Interactive examples of common 2D vec"
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.css",
    "chars": 488,
    "preview": "/* Frank Poth 11/15/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.html",
    "chars": 731,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <link href = \"walk-on-tiles.css\" rel = \"stylesheet\" type = \"text/css\">\r\n   "
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.js",
    "chars": 8758,
    "preview": "// Frank Poth 11/15/2017\r\n\r\n(function() { \"use strict\" // used to throw a compiler error if a variable is not propperly "
  },
  {
    "path": "content/web-app/manifest.json",
    "chars": 508,
    "preview": "{\r\n\r\n  \"author\": \"PoP Vlog\",\r\n  \"background_color\": \"#ffffff\",\r\n  \"description\": \"Progressive Web App Example\",\r\n  \"disp"
  },
  {
    "path": "content/web-app/server.js",
    "chars": 2581,
    "preview": "// Frank Poth 10/16/2017\r\n\r\n(function() {\r\n\r\n  var fs, https, mimetypes, options, path, server;\r\n\r\n  fs = require(\"fs\");"
  },
  {
    "path": "content/web-app/web-app.css",
    "chars": 480,
    "preview": "/* Frank Poth 10/17/2017 */\r\n\r\n* {\r\n\r\n  box-sizing:border-box;\r\n  margin:0;\r\n  padding:0;\r\n\r\n}\r\n\r\nhtml {\r\n\r\n  height:100"
  },
  {
    "path": "content/web-app/web-app.html",
    "chars": 795,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <title>Web App</title>\r\n\r\n    <meta name = \"viewport\" content = \"user-scala"
  },
  {
    "path": "content/wmw-basic/basic.html",
    "chars": 1198,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>WMW-Bas"
  },
  {
    "path": "content/wmw-bouncing-balls/bouncing-balls.html",
    "chars": 2898,
    "preview": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>Ball "
  },
  {
    "path": "data/logs.json",
    "chars": 1948,
    "preview": "[\r\n  {\r\n    \"name\":\"I'm Still Alive!\",\r\n    \"note\":\"I know it's been a while since my last post, but I'm still out here "
  },
  {
    "path": "data/projects.json",
    "chars": 14207,
    "preview": "[\r\n  {\r\n    \"name\":\"Stay Down\",\r\n    \"note\":\"Try to stay down while collecting the items. Keyboard controls.\",\r\n    \"pat"
  },
  {
    "path": "index.css",
    "chars": 641,
    "preview": "/* Frank Poth 2019-11-25 */\r\n\r\n#main-filter-bar {\r\n\r\n  display:flex;\r\n  margin:8px 0px 0px 0px;\r\n  padding:4px 0px 4px 0"
  },
  {
    "path": "index.html",
    "chars": 3735,
    "preview": "<!DOCTYPE html>\r\n\r\n<html lang = \"en\">\r\n\r\n  <head>\r\n\r\n    <link href = \"media/consola.ttf\" rel = \"stylesheet\" type = \"app"
  },
  {
    "path": "index.js",
    "chars": 2608,
    "preview": "(() => {\r\n\r\n  const filter_query       = document.getElementById(\"main-filter-query\");\r\n  const project_container  = doc"
  },
  {
    "path": "library/dom-kit.js",
    "chars": 1166,
    "preview": "// Frank Poth 08/18/2017\r\n\r\nconst DOMKit = function() {};\r\n\r\nDOMKit.createElement = function(tag_name, attributes, conte"
  },
  {
    "path": "log.css",
    "chars": 523,
    "preview": "/* Frank Poth 2019-12-29 */\r\n\r\n.log {\r\n\r\n  display:block;\r\n  max-width:720px;\r\n  padding:8px;\r\n\r\n}\r\n\r\n.log-date {\r\n  \r\n "
  },
  {
    "path": "project.css",
    "chars": 795,
    "preview": "/* Frank Poth 2019-12-29 */\r\n\r\n.project {\r\n\r\n  display:block;\r\n  margin:8px;\r\n  max-width:720px;\r\n\r\n}\r\n\r\n.project-link {"
  },
  {
    "path": "robots.txt",
    "chars": 43,
    "preview": "User-agent: * \r\nDisallow: \r\nCrawl-delay: 15"
  },
  {
    "path": "server.js",
    "chars": 1991,
    "preview": "// Frank Poth 10/16/2017\r\n\r\n(function() {\r\n\r\n  const ip = process.env.MY_IP || \"127.0.0.1\";\r\n  const port = process.env."
  },
  {
    "path": "theme.css",
    "chars": 4012,
    "preview": "/* Frank Poth 2019-12-29 */\r\n\r\n:root {\r\n\r\n  --color-dark-yellow:#c09000;\r\n  --color-dark-blue:#202840;\r\n  --color-light-"
  },
  {
    "path": "tools/log.js",
    "chars": 309,
    "preview": "const Log = function(data) {\r\n\r\n  this.data    = data;\r\n  this.element = document.createRange().createContextualFragment"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the pothonprogramming/pothonprogramming.github.io GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 204 files (594.8 KB), approximately 160.8k tokens, and a symbol index with 114 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!