[
  {
    "path": "content/2018/minkowski-difference/minkowski-difference.html",
    "content": "<!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 Arbitrary Convex Shapes.\">\r\n    <meta name = \"viewport\" content = \"width=device-width, user-scalable=no\">\r\n\r\n    <title>Minkowski Difference</title>\r\n\r\n    <style>\r\n\r\n      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }\r\n\r\n      body, html { height:100%; width:100%; }\r\n\r\n      body { background-color:#202830; display:grid; }\r\n\r\n      div {\r\n\r\n        display:grid;\r\n        grid-template-columns:auto auto auto;\r\n        left:16px;\r\n        position:fixed;\r\n        right:16px;\r\n        top:16px;\r\n\r\n      }\r\n\r\n      a {\r\n\r\n        align-self:center;\r\n        background-color:#202830;\r\n        border-radius:16px;\r\n        color:#ffffff;\r\n        cursor:pointer;\r\n        font-size:1.5em;\r\n        font-weight:700;\r\n        justify-self:center;\r\n        padding:8px 32px;\r\n        pointer-events:auto;\r\n        text-align:center;\r\n\r\n\r\n      }\r\n\r\n      canvas { align-self:center; display:grid; justify-self:center; }\r\n    \r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <div>\r\n      <a>circle</a>\r\n      <a>polygon</a>\r\n      <a>rectangle</a>\r\n    </div>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n        /////////////////\r\n       //// CLASSES ////\r\n      /////////////////\r\n\r\n      class Point2D {\r\n\r\n        constructor(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n        setPosition(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n        setPositionPlus(x, y) {\r\n\r\n          this.x += x;\r\n          this.y += y;\r\n\r\n        }\r\n\r\n      }\r\n      \r\n      class Circle2D {\r\n\r\n        constructor(x, y, radius) {\r\n\r\n          this.position = new Point2D(x, y);\r\n          this.radius = radius;\r\n          this.color = \"rgba(0, 0, 0, 0.5)\";\r\n\r\n        }\r\n\r\n        static createMinkowskiDifferenceShape(circle_a, circle_b) {\r\n\r\n          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);\r\n\r\n        }\r\n\r\n        draw(context, offset) {\r\n\r\n          context.beginPath();\r\n          context.arc(this.position.x + offset.x, this.position.y + offset.y, this.radius, 0, Math.PI * 2, false);\r\n          context.fillStyle = this.color;\r\n          context.fill();\r\n\r\n        }\r\n\r\n        setPosition(x, y) {\r\n\r\n          this.position.setPosition(x, y);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Polygon2D {\r\n\r\n        constructor(x, y, ...vertices) {\r\n          \r\n          this.position = new Point2D(x, y);\r\n          this.vertices = new Array();\r\n          this.color = \"rgba(0, 0, 0, 0.5)\";\r\n\r\n          for (let index = vertices.length - 2; index > -1; index -= 2) {\r\n\r\n            this.vertices[index * 0.5] = new Point2D(vertices[index] + x, vertices[index + 1] + y);\r\n\r\n          }\r\n\r\n        }\r\n\r\n        static createMinkowskiDifferenceShape(polygon_a, polygon_b) {\r\n\r\n          var vertices = new Array();\r\n          \r\n          for (var vertex_a of polygon_a.vertices) {\r\n\r\n            for (var vertex_b of polygon_b.vertices) {\r\n\r\n              vertices.push(vertex_a.x - vertex_b.x, vertex_a.y - vertex_b.y);\r\n\r\n            }\r\n\r\n          }\r\n\r\n          return new Polygon2D(0, 0, ...vertices);\r\n\r\n        }\r\n\r\n        draw(context, offset) {\r\n\r\n          var vertex = this.vertices[0];\r\n\r\n          context.beginPath();\r\n          context.fillStyle = this.color;\r\n          context.moveTo(vertex.x + offset.x, vertex.y + offset.y);\r\n\r\n          for (var vertex of this.vertices) {\r\n            \r\n            context.lineTo(vertex.x + offset.x, vertex.y + offset.y);\r\n            \r\n          }\r\n          \r\n          context.closePath();\r\n          context.fill();\r\n\r\n        }\r\n\r\n        setPosition(x, y) {\r\n\r\n          var vector_x = x - this.position.x;\r\n          var vector_y = y - this.position.y;\r\n\r\n          for (var vertex of this.vertices) {\r\n\r\n            vertex.setPositionPlus(vector_x, vector_y);\r\n\r\n          }\r\n\r\n          this.position.setPositionPlus(vector_x, vector_y);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Rectangle2D {\r\n\r\n        constructor(x, y, width, height) {\r\n\r\n          this.position = new Point2D(x, y);\r\n          this.width = width; this.height = height;\r\n          this.color = \"rgba(0, 0, 0, 0.5)\";\r\n\r\n        }\r\n\r\n        static createMinkowskiDifferenceShape(rectangle_a, rectangle_b) {\r\n\r\n          var x = rectangle_a.position.x - rectangle_b.position.x - rectangle_b.width;\r\n          var y = rectangle_a.position.y - rectangle_b.position.y - rectangle_b.height;\r\n\r\n          return new Rectangle2D(x, y, rectangle_a.width + rectangle_b.width, rectangle_a.height + rectangle_b.height);\r\n\r\n        }\r\n\r\n        draw(context, offset) {\r\n\r\n          context.beginPath();\r\n          context.rect(this.position.x + offset.x, this.position.y + offset.y, this.width, this.height);\r\n          context.fillStyle = this.color;\r\n          context.fill();\r\n\r\n        }\r\n\r\n        setPosition(x, y) {\r\n\r\n          this.position.setPosition(x, y);\r\n\r\n        }\r\n\r\n      }\r\n\r\n        ///////////////////////////\r\n       //// APPLICATION LOGIC ////\r\n      ///////////////////////////\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n      var offset = new Point2D(undefined, undefined); // Offset of the on screen origin\r\n      var pointer = {\r\n\r\n        x:0, y:0\r\n\r\n      };\r\n\r\n      var circles = [\r\n\r\n        new Circle2D(100, 100, 100),\r\n        new Circle2D(200, 200, 100)\r\n\r\n      ];\r\n\r\n      var rectangles = [\r\n\r\n        new Rectangle2D(100, 120, 100, 70),\r\n        new Rectangle2D(150, 175, 80, 50)\r\n\r\n      ];\r\n\r\n      var polygons = [\r\n        \r\n        new Polygon2D(100, 100, -100, 50, 0, -100, 100, 0, 0, 100),\r\n        new Polygon2D(100, 100, -100, 0, 0, -100, 100, 0, 0, 100)\r\n\r\n      ];\r\n\r\n      var shapes = circles;\r\n\r\n      function cycle(time_stamp) {\r\n\r\n        window.requestAnimationFrame(cycle);\r\n\r\n        update();\r\n        render();\r\n\r\n      }\r\n\r\n      function render() {\r\n\r\n        // Fill the entire background\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        // Draw the x and y axes\r\n        context.beginPath();\r\n        context.strokeStyle = \"#202830\";\r\n        context.moveTo(offset.x, 0);\r\n        context.lineTo(offset.x, context.canvas.height);\r\n        context.stroke();\r\n        context.moveTo(0, offset.y);\r\n        context.lineTo(context.canvas.width, offset.y);\r\n        context.stroke();\r\n\r\n        // Draw the shapes\r\n        for (var shape of shapes) {\r\n\r\n          shape.draw(context, offset);\r\n\r\n        }\r\n\r\n        shape.setPosition(pointer.x, pointer.y);\r\n\r\n        var minkowski_difference_shape = shape.constructor.createMinkowskiDifferenceShape(shape, shapes[0]);\r\n\r\n        minkowski_difference_shape.draw(context, offset);\r\n   \r\n      }\r\n\r\n      function update() {\r\n\r\n\r\n      }\r\n\r\n        ////////////////////////\r\n       //// EVENT HANDLERS ////\r\n      ////////////////////////\r\n\r\n      function clickA(event) { event.preventDefault();\r\n\r\n        switch(this.innerText) {\r\n\r\n          case \"circle\":    shapes = circles;    break;\r\n          case \"polygon\":   shapes = polygons;   break;\r\n          case \"rectangle\": shapes = rectangles;\r\n\r\n        }\r\n\r\n        shapes[0].setPosition(Math.floor(Math.random() * context.canvas.width * 0.5), Math.floor(Math.random() * context.canvas.height * 0.5));\r\n\r\n      }\r\n\r\n      // Get the position of the pointer and don't forget to add the offset.\r\n      function mouseMove(event) { event.preventDefault();\r\n\r\n        var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - rectangle.left - offset.x;\r\n        pointer.y = event.clientY - rectangle.top  - offset.y;\r\n\r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.width  = document.documentElement.clientWidth - 16;\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n\r\n        context.canvas.imageSmoothingEnabled = false;\r\n\r\n        offset.x = Math.round(context.canvas.width * 0.5);\r\n        offset.y = Math.round(context.canvas.height * 0.5);\r\n\r\n      }\r\n\r\n        ////////////////////\r\n       //// INITIALIZE ////\r\n      ////////////////////\r\n\r\n      document.querySelectorAll(\"a\").forEach((a) => {\r\n\r\n        a.addEventListener(\"click\", clickA);\r\n\r\n      }); // Add the click event listener to each a element.\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      window.addEventListener(\"mousemove\", mouseMove);\r\n\r\n      resize(); // Make the canvas fit the screen\r\n\r\n      cycle(); // Start the application loop\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/animation/animation.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto;\r\n  height:100%;\r\n  justify-items:center;\r\n  padding:0 8px;\r\n  width:100%;\r\n\r\n}\r\n"
  },
  {
    "path": "content/animation/animation.html",
    "content": "<!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 = \"description\" content = \"A working example of sprite animation.\">\r\n\r\n    <link href = \"animation.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Sprite Animation</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>Use the keyboard to walk left and right. Also, press up to jump.</p>\r\n\r\n    <script src = \"animation.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/animation/animation.js",
    "content": "// 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 Animation class that handles updating and changing a sprite's current\r\nframe, and a sprite_sheet object to hold the source image and the different animation\r\nframe sets. */\r\n\r\n(function() { \"use strict\";\r\n\r\n  /* Each sprite sheet tile is 16x16 pixels in dimension. */\r\n  const SPRITE_SIZE = 16;\r\n\r\n  /* The Animation class manages frames within an animation frame set. The frame\r\n  set is an array of values that correspond to the location of sprite images in\r\n  the sprite sheet. For example, a frame value of 0 would correspond to the first\r\n  sprite image / tile in the sprite sheet. By arranging these values in a frame set\r\n  array, you can create a sequence of frames that make an animation when played in\r\n  quick succession. */\r\n  var Animation = function(frame_set, delay) {\r\n\r\n    this.count = 0;// Counts the number of game cycles since the last frame change.\r\n    this.delay = delay;// The number of game cycles to wait until the next frame change.\r\n    this.frame = 0;// The value in the sprite sheet of the sprite image / tile to display.\r\n    this.frame_index = 0;// The frame's index in the current animation frame set.\r\n    this.frame_set = frame_set;// The current animation frame set that holds sprite tile values.\r\n\r\n  };\r\n\r\n  Animation.prototype = {\r\n\r\n    /* This changes the current animation frame set. For example, if the current\r\n    set is [0, 1], and the new set is [2, 3], it changes the set to [2, 3]. It also\r\n    sets the delay. */\r\n    change:function(frame_set, delay = 15) {\r\n\r\n      if (this.frame_set != frame_set) {// If the frame set is different:\r\n\r\n        this.count = 0;// Reset the count.\r\n        this.delay = delay;// Set the delay.\r\n        this.frame_index = 0;// Start at the first frame in the new frame set.\r\n        this.frame_set = frame_set;// Set the new frame set.\r\n        this.frame = this.frame_set[this.frame_index];// Set the new frame value.\r\n\r\n      }\r\n\r\n    },\r\n\r\n    /* Call this on each game cycle. */\r\n    update:function() {\r\n\r\n      this.count ++;// Keep track of how many cycles have passed since the last frame change.\r\n\r\n      if (this.count >= this.delay) {// If enough cycles have passed, we change the frame.\r\n\r\n        this.count = 0;// Reset the count.\r\n        /* If the frame index is on the last value in the frame set, reset to 0.\r\n        If the frame index is not on the last value, just add 1 to it. */\r\n        this.frame_index = (this.frame_index == this.frame_set.length - 1) ? 0 : this.frame_index + 1;\r\n        this.frame = this.frame_set[this.frame_index];// Change the current frame value.\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var buffer, controller, display, loop, player, render, resize, sprite_sheet;\r\n\r\n  buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n  display = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n  /* I made some changes to the controller object. */\r\n  controller = {\r\n\r\n    /* Now each key object knows its physical state as well as its active state.\r\n    When a key is active it is used in the game logic, but its physical state is\r\n    always recorded and never altered for reference. */\r\n    left:  { active:false, state:false },\r\n    right: { active:false, state:false },\r\n    up:    { active:false, state:false },\r\n\r\n    keyUpDown:function(event) {\r\n\r\n      /* Get the physical state of the key being pressed. true = down false = up*/\r\n      var key_state = (event.type == \"keydown\") ? true : false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37:// left key\r\n\r\n          /* If the virtual state of the key is not equal to the physical state\r\n          of the key, we know something has changed, and we must update the active\r\n          state of the key. By doing this it prevents repeat firing of keydown events\r\n          from altering the active state of the key. Basically, when you are jumping,\r\n          holding the jump key down isn't going to work. You'll have to hit it every\r\n          time, but only if you set the active key state to false when you jump. */\r\n          if (controller.left.state != key_state) controller.left.active = key_state;\r\n          controller.left.state  = key_state;// Always update the physical state.\r\n\r\n        break;\r\n        case 38:// up key\r\n\r\n          if (controller.up.state != key_state) controller.up.active = key_state;\r\n          controller.up.state  = key_state;\r\n\r\n        break;\r\n        case 39:// right key\r\n\r\n          if (controller.right.state != key_state) controller.right.active = key_state;\r\n          controller.right.state  = key_state;\r\n\r\n        break;\r\n\r\n      }\r\n\r\n      //console.log(\"left:  \" + controller.left.state + \", \" + controller.left.active + \"\\nright: \" + controller.right.state + \", \" + controller.right.active + \"\\nup:    \" + controller.up.state + \", \" + controller.up.active);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* The player object is just a rectangle with an animation object. */\r\n  player = {\r\n\r\n    animation:new Animation(),// You don't need to setup Animation right away.\r\n    jumping:true,\r\n    height:16,    width:16,\r\n    x:0,          y:40 - 18,\r\n    x_velocity:0, y_velocity:0\r\n\r\n  };\r\n\r\n  /* The sprite sheet object holds the sprite sheet graphic and some animation frame\r\n  sets. An animation frame set is just an array of frame values that correspond to\r\n  each sprite image in the sprite sheet, just like a tile sheet and a tile map. */\r\n  sprite_sheet = {\r\n\r\n    frame_sets:[[0, 1], [2, 3], [4, 5]],// standing still, walk right, walk left\r\n    image:new Image()\r\n\r\n  };\r\n\r\n  loop = function(time_stamp) {\r\n\r\n    if (controller.up.active && !player.jumping) {\r\n\r\n      controller.up.active = false;\r\n      player.jumping = true;\r\n      player.y_velocity -= 2.5;\r\n\r\n    }\r\n\r\n    if (controller.left.active) {\r\n\r\n      /* To change the animation, all you have to do is call animation.change. */\r\n      player.animation.change(sprite_sheet.frame_sets[2], 15);\r\n      player.x_velocity -= 0.05;\r\n\r\n    }\r\n\r\n    if (controller.right.active) {\r\n\r\n      player.animation.change(sprite_sheet.frame_sets[1], 15);\r\n      player.x_velocity += 0.05;\r\n\r\n    }\r\n\r\n    /* If you're just standing still, change the animation to standing still. */\r\n    if (!controller.left.active && !controller.right.active) {\r\n\r\n      player.animation.change(sprite_sheet.frame_sets[0], 20);\r\n\r\n    }\r\n\r\n    player.y_velocity += 0.25;\r\n\r\n    player.x += player.x_velocity;\r\n    player.y += player.y_velocity;\r\n    player.x_velocity *= 0.9;\r\n    player.y_velocity *= 0.9;\r\n\r\n    if (player.y + player.height > buffer.canvas.height - 2) {\r\n\r\n      player.jumping = false;\r\n      player.y = buffer.canvas.height - 2 - player.height;\r\n      player.y_velocity = 0;\r\n\r\n    }\r\n\r\n    if (player.x + player.width < 0) {\r\n\r\n      player.x = buffer.canvas.width;\r\n\r\n    } else if (player.x > buffer.canvas.width) {\r\n\r\n      player.x = - player.width;\r\n\r\n    }\r\n\r\n    player.animation.update();\r\n\r\n    render();\r\n\r\n    window.requestAnimationFrame(loop);\r\n\r\n  };\r\n\r\n  render = function() {\r\n\r\n    /* Draw the background. */\r\n    buffer.fillStyle = \"#7ec0ff\";\r\n    buffer.fillRect(0, 0, buffer.canvas.width, buffer.canvas.height);\r\n    buffer.strokeStyle = \"#8ed0ff\";\r\n    buffer.lineWidth = 10;\r\n    buffer.beginPath();\r\n    buffer.moveTo(0, 0);\r\n    buffer.bezierCurveTo(40, 20, 40, 0, 80, 0);\r\n    buffer.moveTo(0, 0);\r\n    buffer.bezierCurveTo(40, 20, 40, 20, 80, 0);\r\n    buffer.stroke();\r\n    buffer.fillStyle = \"#009900\";\r\n    buffer.fillRect(0, 36, buffer.canvas.width, 4);\r\n\r\n    /* When you draw your sprite, just use the animation frame value to determine\r\n    where to cut your image from the sprite sheet. It's the same technique used\r\n    for cutting tiles out of a tile sheet. Here I have a very easy implementation\r\n    set up because my sprite sheet is only a single row. */\r\n\r\n    /* 02/07/2018 I added Math.floor to the player's x and y positions to eliminate\r\n    antialiasing issues. Take out the Math.floor to see what I mean. */\r\n    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);\r\n\r\n    display.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, display.canvas.width, display.canvas.height);\r\n\r\n  };\r\n\r\n  resize = function() {\r\n\r\n    display.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n    if (display.canvas.width > document.documentElement.clientHeight) {\r\n\r\n      display.canvas.width = document.documentElement.clientHeight;\r\n\r\n    }\r\n\r\n    display.canvas.height = display.canvas.width * 0.5;\r\n\r\n    display.imageSmoothingEnabled = false;\r\n\r\n  };\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  buffer.canvas.width = 80;\r\n  buffer.canvas.height = 40;\r\n\r\n  window.addEventListener(\"resize\", resize);\r\n\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n\r\n  resize();\r\n\r\n  sprite_sheet.image.addEventListener(\"load\", function(event) {// When the load event fires, do this:\r\n\r\n    window.requestAnimationFrame(loop);// Start the game loop.\r\n\r\n  });\r\n\r\n  sprite_sheet.image.src = \"animation.png\";// Start loading the image.\r\n\r\n})();\r\n"
  },
  {
    "path": "content/animation-game-loop/animation.css",
    "content": "/* 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  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-content:center;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n\r\n}\r\n"
  },
  {
    "path": "content/animation-game-loop/animation.html",
    "content": "<!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>Animation</title>\r\n    <link href = \"animation.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Animation</h1>\r\n    <canvas></canvas>\r\n\r\n    <script src = \"animation.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/animation-game-loop/animation.js",
    "content": "// Frank Poth 08/12/2017\r\n\r\nvar context, rectangle, loop;\r\n\r\ncontext = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\ncontext.canvas.height = 180;\r\ncontext.canvas.width = 320;\r\n\r\nrectangle = {\r\n\r\n  height:32,\r\n  width:32,\r\n  x:0,\r\n  y:72, // Center of the canvas\r\n\r\n};\r\n\r\nloop = function() {\r\n\r\n  rectangle.x += 1;\r\n\r\n  context.fillStyle = \"#202020\";\r\n  context.fillRect(0, 0, 320, 180);// x, y, width, height\r\n  context.fillStyle = \"#ff0000\";// hex for red\r\n  context.beginPath();\r\n  context.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);\r\n  context.fill();\r\n\r\n  if (rectangle.x > 320) {// if rectangle goes past right boundary\r\n\r\n    rectangle.x = -32;\r\n\r\n  }\r\n\r\n  // call update when the browser is ready to draw again\r\n  window.requestAnimationFrame(loop);\r\n\r\n};\r\n\r\nwindow.requestAnimationFrame(loop);\r\n"
  },
  {
    "path": "content/better-tile/better-tile.html",
    "content": "<!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 Poth\">\r\n    <meta name = \"description\"  content = \"\">\r\n    <meta name = \"theme-color\"  content = \"#202830\">\r\n    <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\">\r\n\r\n    <style>\r\n\r\n      * {\r\n      \r\n        box-sizing:border-box;\r\n        margin:0;\r\n        overflow:hidden;\r\n        padding:0;\r\n        user-select:none;\r\n      \r\n      }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      body {\r\n      \r\n        align-content:center;\r\n        background-color:#202830;\r\n        display:grid;\r\n        justify-content:center;\r\n      \r\n      }\r\n\r\n      canvas { image-rendering:pixelated; image-rendering:-moz-crisp-edges; }\r\n\r\n    </style>\r\n\r\n    <title>Better Tile</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"better-tile.js\"></script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/better-tile/better-tile.js",
    "content": "(() => {\r\n\r\n  // The display canvas' context. Draw the tile buffer here.\r\n  const DISPLAY = document.querySelector('canvas').getContext('2d', { alpha:false, desynchronized:false });\r\n  // The tile buffer canvas' context. Draw individual tiles here.\r\n  const BUFFER  = document.createElement('canvas').getContext('2d', { alpha:false, desynchronized:true });\r\n\r\n  // This is the width and height for every tile.\r\n  const TILE_SIZE = 16;\r\n\r\n  // The TILES object contains \"tile\" objects with keys that correspond to the map values.\r\n  // Each tile object has a color.\r\n  const TILES = {\r\n\r\n    0: { color:'#d8f4f4' }, // sky\r\n    1: { color:'#ffffff' }, // cloud\r\n    2: { color:'#3e611e' }, // grass\r\n    3: { color:'#412823' }  // dirt\r\n\r\n  }\r\n\r\n  // The map holds all the info about the map we will be drawing, including the tile indices array.\r\n  const MAP = {\r\n\r\n    columns: 16,\r\n    rows:    14,\r\n    height:  14 * TILE_SIZE,\r\n    width:   16 * TILE_SIZE,\r\n\r\n    // This is used during image scaling to ensure the rendered image is not skewed.\r\n    width_height_ratio: 16 / 14,\r\n\r\n    // The values in this array correspond to the keys in the TILES object.\r\n    tiles:[1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,\r\n           0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,\r\n           0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,\r\n           0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,\r\n           0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,\r\n           2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,\r\n           3,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,\r\n           3,3,3,3,3,3,3,0,0,0,3,3,3,3,3,3]\r\n\r\n  };\r\n\r\n  // This will render tiles to the buffer.\r\n  function renderTiles() {\r\n\r\n    var map_index = 0; // This represents the position in the MAP.tiles array we're getting our tile value from.\r\n\r\n    // Increment by the actual TILE_SIZE to avoid having to multiply on every iteration.\r\n    for (var top = 0; top < MAP.height; top += TILE_SIZE) {\r\n\r\n      for (var left = 0; left < MAP.width; left += TILE_SIZE) {\r\n\r\n        var tile_value = MAP.tiles[map_index]; // Get the tile value from the map.\r\n        \r\n        var tile = TILES[tile_value]; // Get the specific tile object from the TILES object.\r\n\r\n        BUFFER.fillStyle = tile.color; // Now that we have the tile we can access its properties.\r\n\r\n        BUFFER.fillRect(left, top, TILE_SIZE, TILE_SIZE); // Draw the tile at the left, top position and TILE_SIZE.\r\n\r\n        map_index ++; // Make sure to increment the map_index so we can get the next tile from the map.\r\n\r\n      }\r\n\r\n    }\r\n\r\n  }\r\n\r\n  // Render the buffer to the display.\r\n  // If this example required a game loop or repeated draws, this would be your main rendering function.\r\n  // The benefit of this approach is that you only make 1 drawImage call here instead of 1 call for every tile.\r\n  function renderDisplay() {\r\n\r\n    DISPLAY.drawImage(BUFFER.canvas, 0, 0);\r\n\r\n  }\r\n\r\n  // This function resizes the CSS width and height of the DISPLAY canvas to force it to scale to fit the window.\r\n  function resize(event) {\r\n\r\n    // Get the height and width of the window\r\n    var height = document.documentElement.clientHeight;\r\n    var width  = document.documentElement.clientWidth;\r\n\r\n    // This makes sure the DISPLAY canvas is resized in a way that maintains the MAP's width / height ratio.\r\n    if (width / height < MAP.width_height_ratio) height = Math.floor(width  / MAP.width_height_ratio);\r\n    else                                         width  = Math.floor(height * MAP.width_height_ratio);\r\n\r\n    // This sets the CSS of the DISPLAY canvas to resize it to the scaled height and width.\r\n    DISPLAY.canvas.style.height = height + 'px';\r\n    DISPLAY.canvas.style.width  = width  + 'px';\r\n\r\n  }\r\n\r\n  // Set the initial width and height of the BUFFER and the DISPLAY canvases.\r\n  BUFFER.canvas.width  = DISPLAY.canvas.width  = MAP.width;\r\n  BUFFER.canvas.height = DISPLAY.canvas.height = MAP.height;\r\n\r\n  // To ensure there is no anti-aliasing when drawing to the canvas, set image smoothing to false on both canvases.\r\n  BUFFER.imageSmoothingEnabled = DISPLAY.imageSmoothingEnabled = false;\r\n\r\n  // Draw the individual tiles to the buffer.\r\n  renderTiles();\r\n\r\n  // Draw the BUFFER to the DISPLAY canvas.\r\n  renderDisplay();\r\n\r\n  // Add the resize event listener.\r\n  window.addEventListener('resize', resize);\r\n\r\n  // Calling resize forces the DISPLAY canvas to be scaled by the CSS.\r\n  resize();\r\n\r\n})();"
  },
  {
    "path": "content/better-tile-graphics/better-tile-graphics.html",
    "content": "<!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 Poth\">\r\n    <meta name = \"description\"  content = \"Use a canvas buffer to store a full tile map image to reduce draw calls and save memory.\">\r\n    <meta name = \"theme-color\"  content = \"#202830\">\r\n    <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\">\r\n\r\n    <style>\r\n\r\n      * {\r\n      \r\n        box-sizing:border-box;\r\n        margin:0;\r\n        overflow:hidden;\r\n        padding:0;\r\n        user-select:none;\r\n      \r\n      }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      body {\r\n      \r\n        align-content:center;\r\n        background-color:#202830;\r\n        display:grid;\r\n        justify-content:center;\r\n      \r\n      }\r\n\r\n      canvas { image-rendering:pixelated; image-rendering:-moz-crisp-edges; }\r\n\r\n    </style>\r\n\r\n    <title>Better Tile Graphics</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"better-tile-graphics.js\"></script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/better-tile-graphics/better-tile-graphics.js",
    "content": "(() => {\r\n\r\n  // The display canvas' context. Draw the tile buffer here. It's important not to desynchronize when using CSS to scale.\r\n  const DISPLAY = document.querySelector('canvas').getContext('2d', { alpha:false, desynchronized:false });\r\n  // The tile buffer canvas' context. Draw individual tiles here.\r\n  const BUFFER  = document.createElement('canvas').getContext('2d', { alpha:false, desynchronized:true });\r\n\r\n  // This is the width and height for every tile.\r\n  const TILE_SIZE = 16;\r\n\r\n  // This image will hold the tile sheet once it is loaded.\r\n  const TILE_SHEET_IMAGE = new Image();\r\n\r\n  // The map holds all the info about the map we will be drawing, including the tile indices array.\r\n  const MAP = {\r\n\r\n    columns: 16,\r\n    rows:    14,\r\n    height:  14 * TILE_SIZE,\r\n    width:   16 * TILE_SIZE,\r\n\r\n    // This is used during image scaling to ensure the rendered image is not skewed.\r\n    width_height_ratio: 16 / 14,\r\n\r\n    // The values in this array correspond to the keys in the TILES object.\r\n    tiles:[10,22,22,22,22,22,22,22,22,22,22,22,23,-1, 8,15,\r\n           20, 1, 2, 3, 2, 1, 1, 3, 3, 2, 1, 1, 3,-1, 9,19,\r\n           20,-1, 7, 5, 0, 6, 6,-1,-1,-1,-1,-1, 4, 5, 6,19,\r\n           20,-1, 8,15,16,17,18,-1,-1,-1, 6, 5,15,16,17,10,\r\n           20,-1, 8,19,11,12,23,-1,-1,-1,15,17,10,11,10,10,\r\n           20,-1, 8,19,10,23, 3,-1,-1,-1,21,22,22,22,22,13,\r\n           20,-1, 8,19,20, 2,-1,-1,-1,-1, 3, 2, 1, 1, 2,19,\r\n           20,-1, 8,19,20,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,19,\r\n           20,-1, 8,19,20, 5, 5, 6, 4, 6, 5, 4, 4, 0, 6,19,\r\n           20,-1, 8,19,10,17,16,17,17,16,17,17,17,17,16,10,\r\n           20,-1, 8,21,22,22,22,22,22,22,22,22,22,22,22,10,\r\n           20,-1, 9, 3, 1, 1, 2, 3, 3, 2, 1, 2, 3, 1, 1,19,\r\n           20, 4, 5, 6, 6, 5, 4, 4, 5, 4, 6, 5, 6,-1, 7,19,\r\n           10,16,17,17,16,17,17,17,16,17,17,16,18,-1, 8,19]\r\n\r\n  };\r\n\r\n  // 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.\r\n  function calculateTileSourcePosition(tile_index, tile_sheet_columns) {\r\n\r\n    return {\r\n      \r\n      x:           tile_index % tile_sheet_columns  * TILE_SIZE,\r\n      y:Math.floor(tile_index / tile_sheet_columns) * TILE_SIZE\r\n    \r\n    };\r\n\r\n  }\r\n\r\n  // This will render tiles to the buffer.\r\n  function renderTiles() {\r\n\r\n    var map_index = 0; // This represents the position in the MAP.tiles array we're getting our tile value from.\r\n\r\n    // Increment by the actual TILE_SIZE to avoid having to multiply on every iteration.\r\n    for (var top = 0; top < MAP.height; top += TILE_SIZE) {\r\n\r\n      for (var left = 0; left < MAP.width; left += TILE_SIZE) {\r\n\r\n        var tile_value = MAP.tiles[map_index]; // Get the tile value from the map.\r\n\r\n        map_index ++; // Make sure to increment the map_index so we can get the next tile from the map.\r\n\r\n        if (tile_value == -1) continue; // If the tile space is meant to be empty, skip this iteration.\r\n        \r\n        var tile_source_position = calculateTileSourcePosition(tile_value, 6); // Get the specific tile object from the TILES object.\r\n\r\n        BUFFER.drawImage(TILE_SHEET_IMAGE, tile_source_position.x, tile_source_position.y, TILE_SIZE, TILE_SIZE, left, top, TILE_SIZE, TILE_SIZE);\r\n\r\n      }\r\n\r\n    }\r\n\r\n  }\r\n\r\n  // Render the buffer to the display.\r\n  // If this example required a game loop or repeated draws, this would be your main rendering function.\r\n  // The benefit of this approach is that you only make 1 drawImage call here instead of 1 call for every tile.\r\n  function renderDisplay() {\r\n\r\n    DISPLAY.drawImage(BUFFER.canvas, 0, 0);\r\n\r\n  }\r\n\r\n  // This function resizes the CSS width and height of the DISPLAY canvas to force it to scale to fit the window.\r\n  function resize(event) {\r\n\r\n    // Get the height and width of the window\r\n    var height = document.documentElement.clientHeight;\r\n    var width  = document.documentElement.clientWidth;\r\n\r\n    // This makes sure the DISPLAY canvas is resized in a way that maintains the MAP's width / height ratio.\r\n    if (width / height < MAP.width_height_ratio) height = Math.floor(width  / MAP.width_height_ratio);\r\n    else                                         width  = Math.floor(height * MAP.width_height_ratio);\r\n\r\n    // This sets the CSS of the DISPLAY canvas to resize it to the scaled height and width.\r\n    DISPLAY.canvas.style.height = height + 'px';\r\n    DISPLAY.canvas.style.width  = width  + 'px';\r\n\r\n  }\r\n\r\n  // Set the initial width and height of the BUFFER and the DISPLAY canvases.\r\n  BUFFER.canvas.width  = DISPLAY.canvas.width  = MAP.width;\r\n  BUFFER.canvas.height = DISPLAY.canvas.height = MAP.height;\r\n\r\n  // To ensure there is no anti-aliasing when drawing to the canvas, set image smoothing to false on both canvases.\r\n  BUFFER.imageSmoothingEnabled = DISPLAY.imageSmoothingEnabled = false;\r\n\r\n  // Add the resize event listener.\r\n  window.addEventListener('resize', resize);\r\n\r\n  TILE_SHEET_IMAGE.addEventListener('load', function(event) {\r\n\r\n    // Draw the individual tiles to the buffer.\r\n    renderTiles();\r\n\r\n    // Draw the BUFFER to the DISPLAY canvas.\r\n    renderDisplay();\r\n\r\n    // Calling resize forces the DISPLAY canvas to be scaled by the CSS.\r\n    resize();\r\n\r\n  }, { once:true });\r\n  \r\n  TILE_SHEET_IMAGE.src = 'better-tile-graphics.png';\r\n\r\n})();"
  },
  {
    "path": "content/blit/blit.css",
    "content": "/* 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\nhtml {\r\n\r\n  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-items:center;\r\n  padding:0 8px;\r\n  width:100%;\r\n\r\n}\r\n\r\ndiv {\r\n\r\n  align-content:space-around;\r\n  display:grid;\r\n  grid-column-gap:8px;\r\n  grid-row-gap:8px;\r\n  grid-template-areas:\"input input\" \"button1 button2\" \"average1 average2\" \"output1 output2\";\r\n  grid-template-columns:auto auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  min-width:50%;\r\n\r\n}\r\n\r\n#number-of-tests-input {\r\n\r\n  background-color:rgba(0, 0, 0, 0);\r\n  border:none;\r\n  color:#ffffff;\r\n  font-size:1.0em;\r\n  grid-area:input;\r\n  text-align:center;\r\n\r\n}\r\n\r\n#draw-image-button {\r\n\r\n  grid-area:button1;\r\n\r\n}\r\n\r\n#draw-image-average {\r\n\r\n  grid-area:average1;\r\n\r\n}\r\n\r\n#draw-image-output {\r\n\r\n  grid-area:output1;\r\n\r\n}\r\n\r\n#image-data-button {\r\n\r\n  grid-area:button2;\r\n\r\n}\r\n\r\n#image-data-average {\r\n\r\n  grid-area:average2;\r\n\r\n}\r\n\r\n#image-data-output {\r\n\r\n  grid-area:output2;\r\n\r\n}\r\n\r\n.button {\r\n\r\n  border-color:#ffffff;\r\n  border-radius:16px;\r\n  border-style:solid;\r\n  border-width:1px;\r\n  cursor:pointer;\r\n  padding:4px;\r\n  text-align:center;\r\n\r\n}\r\n\r\n.output {\r\n\r\n  font-size:1.0em;\r\n  height:6.0em;\r\n  overflow-y:auto;\r\n\r\n}\r\n"
  },
  {
    "path": "content/blit/blit.html",
    "content": "<!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 = \"description\" content = \"Is drawImage faster than imageData? See for yourself!\">\r\n\r\n    <link href = \"blit.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Blit Test</h1>\r\n\r\n    <p>Which method is faster: CanvasRenderingContext2D.drawImage or CanvasRenderingContext2D.putImageData?<br>Type clear to clear tests.</p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <div>\r\n\r\n      <input id = \"number-of-tests-input\" type = \"text\" value = \"Number of tests:\">\r\n      <a class = \"button\" id = \"draw-image-button\">Test drawImage</a>\r\n      <a class = \"button\" id = \"image-data-button\">Test imageData</a>\r\n      <p id = \"draw-image-average\">Average (0 sets) 0.0 ms</p>\r\n      <p id = \"image-data-average\">Average (0 sets) 0.0 ms</p>\r\n      <p class = \"output\" id = \"draw-image-output\">Number of tests (0): 0.0 ms</p>\r\n      <p class = \"output\" id = \"image-data-output\">Number of tests (0): 0.0 ms</p>\r\n\r\n    </div>\r\n\r\n    <script src = \"blit.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/blit/blit.js",
    "content": "// Frank Poth 12/27/2017\r\n\r\n/* This program tests drawImage and putImageData efficiency. Basically, I run these\r\nfunctions thousands of times and time them. After some tests it became pretty obvious\r\nthat drawImage is way faster, at least on my version of Chrome, anyway. The speed\r\ndifference increases with the number of tests performed, but drawImage still comes\r\nout ahead. \"Comes out ahead...\" What is this, a horse race? */\r\n\r\n(function() {\r\n\r\n  var averages, buffer, display, drawImage, image, image_context, imageData, number_of_tests, test, ui;\r\n\r\n  averages = [[], []];// Where the averages for each set of tests are stored.\r\n  /* I do the testing on the buffer, but to make the image bigger I draw the final\r\n  image to the display with drawImage, which automatically scales it up. Only the\r\n  individual drawing methods are timed, however, so don't let this distract you. */\r\n  buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n  display = document.querySelector(\"canvas\").getContext(\"2d\");\r\n  image = new Image();// Where the loaded image will be stored.\r\n  image_context = document.createElement(\"canvas\").getContext(\"2d\");\r\n  number_of_tests = 10000;// The default number of tests to run.\r\n\r\n  drawImage = function() {\r\n\r\n    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);\r\n\r\n  };\r\n\r\n  imageData = function() {\r\n\r\n    buffer.putImageData(image_context.getImageData(0, 0, image_context.canvas.width, image_context.canvas.height), 0, 0);\r\n\r\n  };\r\n\r\n  test = function(draw) {\r\n\r\n    /* This is the test. The time is recorded before and after the draw functions\r\n    are called and the difference is displayed to the user. */\r\n    let count = 0;\r\n    let start_time = window.performance.now();\r\n\r\n    while(count < number_of_tests) {\r\n\r\n      count ++;\r\n\r\n      draw();\r\n\r\n    }\r\n\r\n    let accumulated_time = window.performance.now() - start_time;\r\n    let data = \"Number of tests (\" + number_of_tests + \"): \" + accumulated_time + \"ms<br>\";\r\n\r\n    if (draw == drawImage) {\r\n\r\n      ui.draw_image_output.innerHTML = data + ui.draw_image_output.innerHTML;\r\n      averages[0].push(accumulated_time);\r\n\r\n      let number = 0;\r\n      for (let index = averages[0].length - 1; index > -1; -- index) {\r\n\r\n        number += averages[0][index];\r\n\r\n      }\r\n\r\n      number /= averages[0].length;\r\n\r\n      ui.draw_image_average.innerHTML = (averages[0].length > 1) ? \"Average (\" + averages[0].length + \" sets): \" + number + \" ms\" : \"Average (\" + averages[0].length + \" set): \" + number + \" ms\";\r\n\r\n    } else if (draw == imageData) {\r\n\r\n      ui.image_data_output.innerHTML = data + ui.image_data_output.innerHTML;\r\n      averages[1].push(accumulated_time);\r\n\r\n      let number = 0;\r\n      for (let index = averages[1].length - 1; index > -1; -- index) {\r\n\r\n        number += averages[1][index];\r\n\r\n      }\r\n\r\n      number /= averages[1].length;\r\n\r\n      ui.image_data_average.innerHTML = (averages[1].length > 1) ? \"Average (\" + averages[1].length + \" sets): \" + number + \" ms\" : \"Average (\" + averages[1].length + \" set): \" + number + \" ms\";\r\n\r\n    }\r\n\r\n    display.imageSmoothingEnabled = false;\r\n    display.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, display.canvas.width, display.canvas.height);\r\n\r\n  };\r\n\r\n  ui = {\r\n\r\n    draw_image_average:document.getElementById(\"draw-image-average\"),\r\n    draw_image_button:document.getElementById(\"draw-image-button\"),\r\n    draw_image_output:document.getElementById(\"draw-image-output\"),\r\n    image_data_average:document.getElementById(\"image-data-average\"),\r\n    image_data_button:document.getElementById(\"image-data-button\"),\r\n    image_data_output:document.getElementById(\"image-data-output\"),\r\n    number_of_tests_input:document.getElementById(\"number-of-tests-input\"),\r\n\r\n    click:function(event) {\r\n\r\n      switch(this) {\r\n\r\n        case ui.draw_image_button:\r\n\r\n          test(drawImage);\r\n\r\n        break;\r\n        case ui.image_data_button:\r\n\r\n          test(imageData);\r\n\r\n        break;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    change:function(event) {\r\n\r\n      let number = Number.parseInt(this.value);\r\n\r\n      if (!isNaN(number)) {\r\n\r\n        number_of_tests = number;\r\n        this.value = \"Number of tests: (\" + number + \")\";\r\n\r\n      } else if (this.value == \"clear\") {\r\n\r\n        ui.draw_image_output.innerHTML = ui.image_data_output.innerHTML = \"Number of tests (0): 0.0 ms\";\r\n        ui.draw_image_average.innerHTML =  ui.image_data_average.innerHTML =  \"Average(0 sets): 0.0 ms\";\r\n\r\n        averages = [[], []];\r\n\r\n        this.value = \"Number of tests (\" + number_of_tests + \")\";\r\n\r\n      } else {\r\n\r\n        this.value = \"Enter a valid integer\";\r\n\r\n      }\r\n\r\n    },\r\n\r\n    focusInOut:function(event) {\r\n\r\n      switch(event.type) {\r\n\r\n        case \"focusin\":\r\n\r\n          this.value = \"\";\r\n\r\n        break;\r\n        case \"focusout\":\r\n\r\n          if (this.value == \"\") this.value = \"Number of tests (\" + number_of_tests + \")\";\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  //// INITIALIZE ////\r\n\r\n  image.addEventListener(\"load\", function(event) {\r\n\r\n    buffer.canvas.height = this.height;\r\n    buffer.canvas.width = this.width;\r\n    display.canvas.height = this.height * 2;\r\n    display.canvas.width = this.width * 2;\r\n\r\n    image_context.canvas.height = this.height;\r\n    image_context.canvas.width = this.width;\r\n\r\n    image_context.drawImage(this, 0, 0, this.width, this.height, 0, 0, this.width, this.height);\r\n\r\n    ui.draw_image_button.addEventListener(\"click\", ui.click);\r\n    ui.image_data_button.addEventListener(\"click\", ui.click);\r\n\r\n    ui.number_of_tests_input.addEventListener(\"focusin\", ui.focusInOut);\r\n    ui.number_of_tests_input.addEventListener(\"focusout\", ui.focusInOut);\r\n    ui.number_of_tests_input.addEventListener(\"change\", ui.change);\r\n    ui.number_of_tests_input.value = \"Number of tests (\" + number_of_tests + \")\";\r\n\r\n  });\r\n\r\n  image.src = \"blit.png\";\r\n\r\n})();\r\n"
  },
  {
    "path": "content/bouncing-polygons/bouncing-polygons.html",
    "content": "<!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 arbitrary polygon.\">\r\n    <meta name = \"viewport\" content = \"width=device-width, user-scalable=no\">\r\n\r\n    <title>Bouncing Polygons</title>\r\n\r\n    <style>\r\n\r\n      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }\r\n\r\n      body, html { height:100%; width:100%; }\r\n\r\n      body { background-color:#202830; display:grid; }\r\n\r\n      canvas { align-self:center; display:grid; justify-self:center; }\r\n    \r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n    \r\n        /////////////////\r\n       //// CLASSES ////\r\n      /////////////////\r\n\r\n      /* 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. */\r\n      class BottomBoundary {\r\n\r\n        constructor() {\r\n\r\n          this.line_segment = new LineSegment2D(0, 0, 0, 0, 0, 0); // The top surface\r\n          this.minimum_y = undefined; // The highest the points on the segment can go.\r\n          this.middle_y  = undefined; // The middle of the two points on the segment\r\n          this.maximum_y = undefined; // The lowest the points on the segment can go.\r\n          this.tilt_speed = 0.25;\r\n\r\n        }\r\n\r\n        /* Tilt the line segment between minimum and maximum y. */\r\n        update() {\r\n\r\n          this.line_segment.point0.y += this.tilt_speed;\r\n          this.line_segment.point1.y = this.middle_y - this.line_segment.point0.y + this.middle_y;\r\n\r\n          if (this.line_segment.point0.y < this.minimum_y) {\r\n\r\n            this.line_segment.point0.y = this.minimum_y;\r\n            this.tilt_speed = -this.tilt_speed;\r\n\r\n          } else if (this.line_segment.point0.y > this.maximum_y) {\r\n\r\n            this.line_segment.point0.y = this.maximum_y;\r\n            this.tilt_speed = -this.tilt_speed;\r\n\r\n          }\r\n\r\n        }\r\n\r\n      }\r\n\r\n      /* 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. */\r\n      class LineSegment2D {\r\n\r\n        constructor(x, y, x0, y0, x1, y1) {\r\n\r\n          this.position = new Point2D(x, y);\r\n\r\n          this.point0 = new Point2D(x0, y0);\r\n          this.point1 = new Point2D(x1, y1);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Point2D {\r\n\r\n        constructor(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n        /* Moves the point to the specified x and y coordinates. */\r\n        setPosition(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Polygon2D {\r\n\r\n        constructor(x, y, ...vertices) {\r\n          \r\n          this.position = new Point2D(x, y);\r\n          this.rotation = 0;\r\n          this.scale = 1;\r\n          this.vertices = new Array(); // points in the polygon\r\n\r\n          /* Have some extra values to make the polygons move around. */\r\n          this.direction = Math.random() * Math.PI * 2;\r\n          this.speed     = Math.random() * 2 + 1;\r\n          this.rotational_velocity = Math.random() * 0.05 - 0.025;\r\n\r\n          for (let index = vertices.length - 2; index > -1; index -= 2) {\r\n\r\n            this.vertices[index * 0.5] = new Point2D(vertices[index], vertices[index + 1]);\r\n\r\n          }\r\n        \r\n        }\r\n\r\n        /* Get the y value of the point in the polygon that is farthest down on the y axis. */\r\n        getBottom() {\r\n\r\n          var vertex = this.vertices[0];\r\n          var bottom = vertex.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; -- index) {\r\n\r\n            vertex = this.vertices[index];\r\n\r\n            if (vertex.y > bottom) bottom = vertex.y;\r\n\r\n          } return bottom;\r\n\r\n        }\r\n\r\n        /* Get the x value of the point in the polygon that is farthest left on the x axis. */\r\n        getLeft() {\r\n\r\n          var vertex = this.vertices[0];\r\n          var left   = vertex.x;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; -- index) {\r\n\r\n            vertex = this.vertices[index];\r\n\r\n            if (vertex.x < left) left = vertex.x;\r\n\r\n          } return left;\r\n\r\n        }\r\n\r\n        /* Get the point in the polygon that is farthest along the given vector. */\r\n        getFarthestPoint(vector_x, vector_y) {\r\n\r\n          var vertex        = this.vertices[0]; // Start with one of the vertices\r\n          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.\r\n          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.\r\n\r\n          // loop through all points except for the one we already checked.\r\n          for (let index = this.vertices.length - 1; index > 0; -- index) {\r\n\r\n            vertex = this.vertices[index];\r\n\r\n            let test_dot_product = vertex.x * vector_x + vertex.y * vector_y;\r\n\r\n            // 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.\r\n            if (test_dot_product > dot_product) {\r\n\r\n              dot_product = test_dot_product; // set up for next iteration\r\n              farthest_point.x = vertex.x; // set up our new farthest point\r\n              farthest_point.y = vertex.y;\r\n\r\n            }\r\n\r\n          } return farthest_point;\r\n\r\n        }\r\n\r\n        /* Get the x value of the point in the polygon that is farthest right on the x axis. */\r\n        getRight() {\r\n\r\n          var vertex = this.vertices[0];\r\n          var right  = vertex.x;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; -- index) {\r\n\r\n            vertex = this.vertices[index];\r\n\r\n            if (vertex.x > right) right = vertex.x;\r\n\r\n          } return right;\r\n\r\n        }\r\n\r\n        /* Get the y value of the point in the polygon that is farthest up on the y axis. */\r\n        getTop() {\r\n\r\n          var vertex = this.vertices[0];\r\n          var top    = vertex.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; -- index) {\r\n\r\n            vertex = this.vertices[index];\r\n\r\n            if (vertex.y < top) top = vertex.y;\r\n\r\n          } return top;\r\n\r\n        }\r\n\r\n        setPosition(x, y) {\r\n\r\n          var vector_x = x - this.position.x;\r\n          var vector_y = y - this.position.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n            \r\n            vertex.x += vector_x;\r\n            vertex.y += vector_y;\r\n\r\n          }\r\n\r\n          this.position.x += vector_x;\r\n          this.position.y += vector_y;\r\n\r\n        }\r\n\r\n        /* 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. */\r\n        setPositionPlus(x, y) {\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n\r\n            vertex.x += x;\r\n            vertex.y += y;\r\n\r\n          }\r\n\r\n          this.position.x += x;\r\n          this.position.y += y;\r\n\r\n        }\r\n\r\n        setRotation(rotation) {\r\n\r\n          var radians = rotation - this.rotation;\r\n          var unit_x = Math.cos(radians);\r\n          var unit_y = Math.sin(radians);\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n            \r\n            let vector_x = vertex.x - this.position.x;\r\n            let vector_y = vertex.y - this.position.y;\r\n\r\n            vertex.x = vector_x * unit_x - vector_y * unit_y + this.position.x;\r\n            vertex.y = vector_x * unit_y + vector_y * unit_x + this.position.y;\r\n\r\n          }\r\n\r\n          this.rotation = rotation;\r\n\r\n        }\r\n\r\n        setScale(scale) {\r\n\r\n          var ratio = scale / this.scale;\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n\r\n            let vector_x = vertex.x - this.position.x;\r\n            let vector_y = vertex.y - this.position.y;\r\n\r\n            vertex.x = vector_x * ratio + this.position.x;\r\n            vertex.y = vector_y * ratio + this.position.y;\r\n\r\n          }\r\n\r\n          this.scale = scale;\r\n\r\n        }\r\n\r\n        update() {\r\n\r\n          this.setPositionPlus(Math.cos(this.direction) * this.speed, Math.sin(this.direction) * this.speed);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      /* This is the same as the Point2D class except it has vector math functionality in its prototype. */\r\n      class Vector2D {\r\n\r\n        constructor(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n        /* Returns a vector that runs from point0 to point1. */\r\n        static fromPoints(point0, point1) {\r\n\r\n          return new Vector2D(point1.x - point0.x, point1.y - point0.y);\r\n\r\n        }\r\n\r\n        /* Clones the parameter vector. */\r\n        static fromVector(vector2d) {\r\n\r\n          return new Vector2D(vector2d.x, vector2d.y);\r\n\r\n        }\r\n\r\n        /* 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. */\r\n        getCrossProduct(vector2d) {\r\n\r\n          return this.x * vector2d.y - this.y * vector2d.x;\r\n\r\n        }\r\n\r\n        /* We don't use this, but I figured I'd leave it in for reference. */\r\n        toLeftNormal() {\r\n\r\n          var x = this.x;\r\n\r\n          this.x = this.y;\r\n          this.y = -x;\r\n\r\n        }\r\n\r\n        /* 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. */\r\n        toRightNormal() {\r\n          \r\n          var x = this.x;\r\n\r\n          this.x = -this.y;\r\n          this.y = x;\r\n\r\n        }\r\n\r\n      }\r\n\r\n        ///////////////////\r\n       //// VARIABLES ////\r\n      ///////////////////\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\", { alpha:false });\r\n\r\n      var polygons = [new Polygon2D(0, 0, 0, -1, 1, 0, 0.5, 0, 0.5, 1, -0.5, 1, -0.5, 0, -1, 0),\r\n                  new Polygon2D(0, 0, -0.25, -1, -0.25, -0.25, 1, -0.25, 0.25, 1, 0.25, 0.25, -1, 0.25),\r\n                  new Polygon2D(0, 0, -1, 0, -0.5, -0.75, 0.5, -0.75, 1, 0, 0.5, 0.75, -0.5, 0.75),\r\n                  new Polygon2D(0, -0.25, -1, -0.5, 0, -1.5, 1, -0.5, 0.75, 0.75, -0.75, 0.75),\r\n                  new Polygon2D(0, 0, -1, -1, 1, -1, 1, 1, -1, 1),\r\n                  new Polygon2D(0, 0.25, 0, -1, 1, 1, -1, 1)];\r\n\r\n      var bottom_boundary = new BottomBoundary();\r\n\r\n\r\n        ///////////////////\r\n       //// FUNCTIONS ////\r\n      ///////////////////\r\n\r\n      /* 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. */\r\n      function collideBottomBoundary(polygon) {\r\n\r\n        var ab = Vector2D.fromPoints(bottom_boundary.line_segment.point0, bottom_boundary.line_segment.point1);// ab = segment vector\r\n        \r\n        var abn = Vector2D.fromVector(ab); // clone ab\r\n        abn.toRightNormal(); // convert it to the right hand normal\r\n\r\n        var c = polygon.getFarthestPoint(abn.x, abn.y);\r\n\r\n        var ca = Vector2D.fromPoints(c, bottom_boundary.line_segment.point0);\r\n\r\n        if (ca.getCrossProduct(ab) > 0) { // c is below the line segment!\r\n\r\n          // This equation to find the factor will help us find cd.\r\n          var factor = (-ab.x * ca.x - ab.y * ca.y) / (ab.x * ab.x + ab.y * ab.y);\r\n\r\n          var cd = new Vector2D(ca.x + factor * ab.x, ca.y + factor * ab.y);\r\n\r\n          polygon.setPositionPlus(cd.x, cd.y); // move c to d along with the rest of the polygon.\r\n          polygon.direction = Math.atan2(Math.sin(polygon.direction) * -1, Math.cos(polygon.direction)); // reverse the polygon's direction on the y axis.\r\n\r\n        }        \r\n\r\n      }\r\n\r\n      /* 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. */\r\n      function collideRectangularContainer(polygon, left, right, top) {\r\n\r\n        var polygon_left   = polygon.getLeft();\r\n        var polygon_right  = polygon.getRight();\r\n        var polygon_top    = polygon.getTop();\r\n\r\n        if (polygon_left < left) {\r\n\r\n          polygon.setPositionPlus(left - polygon_left, 0);\r\n          polygon.direction = Math.atan2(Math.sin(polygon.direction), Math.cos(polygon.direction) * -1);\r\n\r\n        } else if (polygon_right > right) {\r\n\r\n          polygon.setPositionPlus(right - polygon_right, 0);\r\n          polygon.direction = Math.atan2(Math.sin(polygon.direction), Math.cos(polygon.direction) * -1);\r\n\r\n        }\r\n\r\n        if (polygon_top < top) {\r\n\r\n          polygon.setPositionPlus(0, top - polygon_top);\r\n          polygon.direction = Math.atan2(Math.sin(polygon.direction) * -1, Math.cos(polygon.direction));\r\n\r\n        }\r\n\r\n        // No need for bottom collision because of the bottom boundary.\r\n\r\n      }\r\n\r\n      function drawBottomBoundary(color) {\r\n\r\n        context.beginPath();\r\n        context.moveTo(bottom_boundary.line_segment.point0.x, bottom_boundary.line_segment.point0.y);\r\n        context.lineTo(bottom_boundary.line_segment.point1.x, bottom_boundary.line_segment.point1.y);\r\n        context.lineTo(context.canvas.width, context.canvas.height);\r\n        context.lineTo(0, context.canvas.height);\r\n\r\n        context.fillStyle = color;\r\n        context.fill();\r\n\r\n      }\r\n\r\n      function drawPolygon2D(polygon2d, color) {\r\n\r\n        var vertex = polygon2d.vertices[0];\r\n\r\n        context.beginPath();\r\n        context.moveTo(vertex.x, vertex.y);\r\n\r\n        for (let index = polygon2d.vertices.length - 1; index > 0; -- index) {\r\n\r\n          vertex = polygon2d.vertices[index];\r\n          context.lineTo(vertex.x, vertex.y);\r\n\r\n        }\r\n\r\n        context.fillStyle = color;\r\n        context.fill();\r\n\r\n      }\r\n\r\n      function loop(time_stamp) {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        context.fillStyle = \"#c0f0d8\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        bottom_boundary.update();\r\n\r\n        drawBottomBoundary(\"rgba(0,128,256,0.75\");\r\n\r\n        for (let index = polygons.length - 1; index > -1; -- index) {\r\n\r\n          var polygon = polygons[index];\r\n\r\n          polygon.update();\r\n          polygon.setRotation(polygon.rotation + polygon.rotational_velocity);\r\n\r\n          collideRectangularContainer(polygon, 0, context.canvas.width, 0);\r\n\r\n          collideBottomBoundary(polygon);\r\n\r\n          drawPolygon2D(polygon, \"rgba(0,128,256,0.75)\");\r\n\r\n        }\r\n\r\n        // Draw an Axis Aligned bounding box\r\n        context.beginPath();\r\n        context.rect(polygon.getLeft(), polygon.getTop(), polygon.getRight() - polygon.getLeft(), polygon.getBottom() - polygon.getTop());\r\n        context.strokeStyle = \"rgba(0,128,256,0.75)\";\r\n        context.stroke();\r\n\r\n      }\r\n\r\n        /////////////////////////\r\n       //// EVENT LISTENERS ////\r\n      /////////////////////////\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n        context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n        context.imageSmoothingEnabled = false;\r\n\r\n        bottom_boundary.middle_y = context.canvas.height * 0.8;\r\n        bottom_boundary.minimum_y = context.canvas.height * 0.6;\r\n        bottom_boundary.maximum_y = context.canvas.height;\r\n        bottom_boundary.line_segment.point0.setPosition(0, bottom_boundary.middle_y);\r\n        bottom_boundary.line_segment.point1.setPosition(context.canvas.width, bottom_boundary.middle_y);\r\n\r\n      }\r\n\r\n        ////////////////////\r\n       //// INITIALIZE ////\r\n      ////////////////////\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      resize();\r\n\r\n      /* Move the polygons to the middle of the canvas and resize them. */\r\n      for (let index = polygons.length - 1; index > -1; -- index) {\r\n\r\n        let polygon = polygons[index];\r\n\r\n        polygon.setPosition(context.canvas.width * 0.5, context.canvas.height * 0.5);\r\n        polygon.setScale(Math.floor(Math.random() * 30) + 20);\r\n\r\n      }\r\n\r\n      /* Randomly sort the array of polygons so a different polygon can be drawn with a bounding box each time the program runs. */\r\n      for (let index = polygons.length - 1; index > -1; -- index) {\r\n\r\n        let polygon = polygons.pop();\r\n\r\n        polygons.splice(Math.floor(Math.random() * polygons.length - 1), 0, polygon);\r\n        \r\n      }\r\n\r\n      loop(); // start the game loop.\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/calculator/calculator.css",
    "content": "/* 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\nhtml {\r\n\r\n  --f1-color:#003399;\r\n  --f2-color:#0066cc;\r\n  --f3-color:#0099ff;\r\n\r\n  height:100%; width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:center;\r\n  background-color:#202830;\r\n  display:grid;\r\n  grid-template-columns:100%;\r\n  grid-template-rows:100%;\r\n  justify-content:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\n#calculator {\r\n\r\n  align-self:center;\r\n  background-color:#999999;\r\n  border-radius:16px 16px 8px 8px;\r\n  display:grid;\r\n  grid-column-gap:4px;\r\n  grid-row-gap:4px;\r\n  grid-template-areas:\"screen screen screen screen screen\"\r\n                      \"q      clr    f1     f2     f3\"\r\n                      \"n1     n2     n3     cmd1   cmd2\"\r\n                      \"n4     n5     n6     cmd3   cmd4\"\r\n                      \"n7     n8     n9     cmd5   cmd6\"\r\n                      \"cma    n0     prd    cmd7   cmd8\";\r\n  grid-template-columns:min-content min-content min-content min-content min-content;\r\n  grid-template-rows:2fr 1fr 1fr 1fr 1fr 1fr;\r\n  justify-self:center;\r\n  max-width:100%;\r\n  overflow:hidden;\r\n  padding:4px;\r\n\r\n}\r\n\r\n#calculator-screen {\r\n\r\n  align-items:center;\r\n  background-color:#ffffff;\r\n  border-radius:16px 16px 8px 8px;\r\n  color:#202830;\r\n  display:grid;\r\n  font-size:2.0em;\r\n  grid-area:screen;\r\n  height:128px;\r\n  overflow-y:auto;\r\n  padding:4px 8px;\r\n  text-align:center;\r\n  white-space:wrap;\r\n  word-wrap:break-word;\r\n  word-break:break-all;\r\n  user-select:text;\r\n\r\n}\r\n\r\n#calculator-q        { background-color:#ff9900; grid-area:q; }\r\n#calculator-clr      { background-color:#990000; grid-area:clr; }\r\n#calculator-f1       { background-color:var(--f1-color); grid-area:f1; }\r\n#calculator-f2       { background-color:var(--f2-color); grid-area:f2; }\r\n#calculator-f3       { background-color:var(--f3-color); grid-area:f3; }\r\n#calculator-0        { grid-area:n0;   }\r\n#calculator-1        { grid-area:n1;   }\r\n#calculator-2        { grid-area:n2;   }\r\n#calculator-3        { grid-area:n3;   }\r\n#calculator-4        { grid-area:n4;   }\r\n#calculator-5        { grid-area:n5;   }\r\n#calculator-6        { grid-area:n6;   }\r\n#calculator-7        { grid-area:n7;   }\r\n#calculator-8        { grid-area:n8;   }\r\n#calculator-9        { grid-area:n9;   }\r\n#calculator-cma      { grid-area:cma;  }\r\n#calculator-prd      { grid-area:prd;  }\r\n#calculator-plus     { background-color:var(--f1-color); grid-area:cmd1; }\r\n#calculator-minus    { background-color:var(--f1-color); grid-area:cmd2; }\r\n#calculator-multiply { background-color:var(--f1-color); grid-area:cmd3; }\r\n#calculator-divide   { background-color:var(--f1-color); grid-area:cmd4; }\r\n#calculator-open-parenthesis { background-color:var(--f1-color); grid-area:cmd5; }\r\n#calculator-close-parenthesis { background-color:var(--f1-color); grid-area:cmd6; }\r\n#calculator-pi { background-color:var(--f2-color); grid-area:cmd1; display:none; }\r\n#calculator-pow { background-color:var(--f2-color); grid-area:cmd2; display:none; }\r\n#calculator-sqrt { background-color:var(--f2-color); grid-area:cmd3; display:none; }\r\n#calculator-ln { background-color:var(--f2-color); grid-area:cmd4; display:none; }\r\n#calculator-log { background-color:var(--f2-color); grid-area:cmd5; display:none; }\r\n#calculator-rnd { background-color:var(--f2-color); grid-area:cmd6; display:none; }\r\n#calculator-cos { background-color:var(--f3-color); grid-area:cmd1; display:none; }\r\n#calculator-sin { background-color:var(--f3-color); grid-area:cmd2; display:none; }\r\n#calculator-tan { background-color:var(--f3-color); grid-area:cmd3; display:none; }\r\n#calculator-acos { background-color:var(--f3-color); grid-area:cmd4; display:none; }\r\n#calculator-asin { background-color:var(--f3-color); grid-area:cmd5; display:none; }\r\n#calculator-atan { background-color:var(--f3-color); grid-area:cmd6; display:none; }\r\n\r\n#calculator-del      { background-color:#990000; grid-area:cmd7; }\r\n#calculator-ans      { background-color:#009900; grid-area:cmd8; }\r\n\r\n.calculator-button {\r\n\r\n  align-items:center;\r\n  background-color:#303840;\r\n  border-radius:8px;\r\n  color:#ffffff;\r\n  cursor:pointer;\r\n  display:grid;\r\n  font-size:1.5em;\r\n  height:64px;\r\n  justify-items:center;\r\n  width:64px;\r\n\r\n}\r\n\r\n.calculator-button:hover {\r\n\r\n  background-color:#384048;\r\n\r\n}\r\n"
  },
  {
    "path": "content/calculator/calculator.html",
    "content": "<!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=device-width\">\r\n    <meta name = \"description\" content = \"A simple calculator written in JavaScript by Frank Poth.\">\r\n    <link href = \"calculator.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n    <title>Calculator</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <div id = \"calculator\">\r\n\r\n      <div id = \"calculator-screen\"></div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-clr\">clr</div>\r\n      <div class = \"calculator-button\" id = \"calculator-f1\">f1</div>\r\n      <div class = \"calculator-button\" id = \"calculator-f2\">f2</div>\r\n      <div class = \"calculator-button\" id = \"calculator-f3\">f3</div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-0\">0</div>\r\n      <div class = \"calculator-button\" id = \"calculator-1\">1</div>\r\n      <div class = \"calculator-button\" id = \"calculator-2\">2</div>\r\n      <div class = \"calculator-button\" id = \"calculator-3\">3</div>\r\n      <div class = \"calculator-button\" id = \"calculator-4\">4</div>\r\n      <div class = \"calculator-button\" id = \"calculator-5\">5</div>\r\n      <div class = \"calculator-button\" id = \"calculator-6\">6</div>\r\n      <div class = \"calculator-button\" id = \"calculator-7\">7</div>\r\n      <div class = \"calculator-button\" id = \"calculator-8\">8</div>\r\n      <div class = \"calculator-button\" id = \"calculator-9\">9</div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-plus\">+</div>\r\n      <div class = \"calculator-button\" id = \"calculator-minus\">-</div>\r\n      <div class = \"calculator-button\" id = \"calculator-multiply\">*</div>\r\n      <div class = \"calculator-button\" id = \"calculator-divide\">/</div>\r\n      <div class = \"calculator-button\" id = \"calculator-open-parenthesis\">(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-close-parenthesis\">)</div>\r\n      <div class = \"calculator-button\" id = \"calculator-pi\">PI</div>\r\n      <div class = \"calculator-button\" id = \"calculator-pow\">pow(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-sqrt\">sqrt(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-ln\">ln(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-log\">log(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-rnd\">rnd()</div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-cos\">cos(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-sin\">sin(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-tan\">tan(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-acos\">acos(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-asin\">asin(</div>\r\n      <div class = \"calculator-button\" id = \"calculator-atan\">atan(</div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-prd\">.</div>\r\n      <div class = \"calculator-button\" id = \"calculator-cma\">,</div>\r\n      <div class = \"calculator-button\" id = \"calculator-ans\">ans</div>\r\n      <div class = \"calculator-button\" id = \"calculator-del\">del</div>\r\n\r\n      <div class = \"calculator-button\" id = \"calculator-q\">?</div>\r\n\r\n    </div><!-- calculator -->\r\n\r\n    <script src = \"calculator.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/calculator/calculator.js",
    "content": "// 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 = Math.sqrt;\r\n  const ln = Math.log;\r\n  const log = Math.log10;\r\n  const rnd = Math.random;\r\n  const cos = Math.cos;\r\n  const sin = Math.sin;\r\n  const tan = Math.tan;\r\n  const acos = Math.acos;\r\n  const asin = Math.asin;\r\n  const atan = Math.atan;\r\n\r\n  var controller, resize, ui, update;\r\n\r\n  controller = {\r\n\r\n    active:false, state:false, value:\"\",\r\n\r\n    click:function(event) {\r\n\r\n      controller.active = true;\r\n      controller.value = this.innerHTML;\r\n\r\n      update();\r\n\r\n    },\r\n\r\n    keyPress:function(event) {\r\n\r\n      if (!event.repeat) {\r\n\r\n        controller.active = true;\r\n        controller.value = String(event.key);\r\n\r\n      }\r\n\r\n      update();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  ui = {\r\n\r\n    calculator:document.getElementById(\"calculator\"),\r\n    screen:document.getElementById(\"calculator-screen\"),\r\n\r\n    buttons: {\r\n\r\n      \"?\":document.getElementById(\"calculator-q\"),\r\n      \"clr\":document.getElementById(\"calculator-clr\"),\r\n      \"f1\":document.getElementById(\"calculator-f1\"),\r\n      \"f2\":document.getElementById(\"calculator-f2\"),\r\n      \"f3\":document.getElementById(\"calculator-f3\"),\r\n\r\n      \"0\":document.getElementById(\"calculator-0\"),\r\n      \"1\":document.getElementById(\"calculator-1\"),\r\n      \"2\":document.getElementById(\"calculator-2\"),\r\n      \"3\":document.getElementById(\"calculator-3\"),\r\n      \"4\":document.getElementById(\"calculator-4\"),\r\n      \"5\":document.getElementById(\"calculator-5\"),\r\n      \"6\":document.getElementById(\"calculator-6\"),\r\n      \"7\":document.getElementById(\"calculator-7\"),\r\n      \"8\":document.getElementById(\"calculator-8\"),\r\n      \"9\":document.getElementById(\"calculator-9\"),\r\n\r\n      \"+\":document.getElementById(\"calculator-plus\"),\r\n      \"-\":document.getElementById(\"calculator-minus\"),\r\n      \"/\":document.getElementById(\"calculator-divide\"),\r\n      \"*\":document.getElementById(\"calculator-multiply\"),\r\n      \"(\":document.getElementById(\"calculator-open-parenthesis\"),\r\n      \")\":document.getElementById(\"calculator-close-parenthesis\"),\r\n      \"PI\":document.getElementById(\"calculator-pi\"),\r\n      \"pow(\":document.getElementById(\"calculator-pow\"),\r\n      \"sqrt(\":document.getElementById(\"calculator-sqrt\"),\r\n      \"cos(\":document.getElementById(\"calculator-cos\"),\r\n      \"sin(\":document.getElementById(\"calculator-sin\"),\r\n      \"tan(\":document.getElementById(\"calculator-tan\"),\r\n      \"acos(\":document.getElementById(\"calculator-acos\"),\r\n      \"asin(\":document.getElementById(\"calculator-asin\"),\r\n      \"atan(\":document.getElementById(\"calculator-atan\"),\r\n      \"ln(\":document.getElementById(\"calculator-ln\"),\r\n      \"log(\":document.getElementById(\"calculator-log\"),\r\n      \"rnd()\":document.getElementById(\"calculator-rnd\"),\r\n      \",\":document.getElementById(\"calculator-cma\"),\r\n      \".\":document.getElementById(\"calculator-prd\"),\r\n      \"del\":document.getElementById(\"calculator-del\"),\r\n      \"ans\":document.getElementById(\"calculator-ans\"),\r\n\r\n    },\r\n\r\n    hitF1:function() {\r\n\r\n      this.buttons[\"+\"].style.display = \"grid\";\r\n      this.buttons[\"-\"].style.display = \"grid\";\r\n      this.buttons[\"*\"].style.display = \"grid\";\r\n      this.buttons[\"/\"].style.display = \"grid\";\r\n      this.buttons[\"(\"].style.display = \"grid\";\r\n      this.buttons[\")\"].style.display = \"grid\";\r\n\r\n      this.buttons[\"PI\"].style.display = \"none\";\r\n      this.buttons[\"pow(\"].style.display = \"none\";\r\n      this.buttons[\"sqrt(\"].style.display = \"none\";\r\n      this.buttons[\"ln(\"].style.display = \"none\";\r\n      this.buttons[\"log(\"].style.display = \"none\";\r\n      this.buttons[\"rnd()\"].style.display = \"none\";\r\n      this.buttons[\"cos(\"].style.display = \"none\";\r\n      this.buttons[\"sin(\"].style.display = \"none\";\r\n      this.buttons[\"tan(\"].style.display = \"none\";\r\n      this.buttons[\"acos(\"].style.display = \"none\";\r\n      this.buttons[\"asin(\"].style.display = \"none\";\r\n      this.buttons[\"atan(\"].style.display = \"none\";\r\n\r\n    },\r\n\r\n    hitF2:function() {\r\n\r\n      this.buttons[\"+\"].style.display = \"none\";\r\n      this.buttons[\"-\"].style.display = \"none\";\r\n      this.buttons[\"*\"].style.display = \"none\";\r\n      this.buttons[\"/\"].style.display = \"none\";\r\n      this.buttons[\"(\"].style.display = \"none\";\r\n      this.buttons[\")\"].style.display = \"none\";\r\n      this.buttons[\"cos(\"].style.display = \"none\";\r\n      this.buttons[\"sin(\"].style.display = \"none\";\r\n      this.buttons[\"tan(\"].style.display = \"none\";\r\n      this.buttons[\"acos(\"].style.display = \"none\";\r\n      this.buttons[\"asin(\"].style.display = \"none\";\r\n      this.buttons[\"atan(\"].style.display = \"none\";\r\n\r\n      this.buttons[\"PI\"].style.display = \"grid\";\r\n      this.buttons[\"pow(\"].style.display = \"grid\";\r\n      this.buttons[\"sqrt(\"].style.display = \"grid\";\r\n      this.buttons[\"ln(\"].style.display = \"grid\";\r\n      this.buttons[\"log(\"].style.display = \"grid\";\r\n      this.buttons[\"rnd()\"].style.display = \"grid\";\r\n\r\n    },\r\n\r\n    hitF3:function() {\r\n\r\n      this.buttons[\"+\"].style.display = \"none\";\r\n      this.buttons[\"-\"].style.display = \"none\";\r\n      this.buttons[\"*\"].style.display = \"none\";\r\n      this.buttons[\"/\"].style.display = \"none\";\r\n      this.buttons[\"(\"].style.display = \"none\";\r\n      this.buttons[\")\"].style.display = \"none\";\r\n      this.buttons[\"PI\"].style.display = \"none\";\r\n      this.buttons[\"pow(\"].style.display = \"none\";\r\n      this.buttons[\"sqrt(\"].style.display = \"none\";\r\n      this.buttons[\"ln(\"].style.display = \"none\";\r\n      this.buttons[\"log(\"].style.display = \"none\";\r\n      this.buttons[\"rnd()\"].style.display = \"none\";\r\n\r\n      this.buttons[\"cos(\"].style.display = \"grid\";\r\n      this.buttons[\"sin(\"].style.display = \"grid\";\r\n      this.buttons[\"tan(\"].style.display = \"grid\";\r\n      this.buttons[\"acos(\"].style.display = \"grid\";\r\n      this.buttons[\"asin(\"].style.display = \"grid\";\r\n      this.buttons[\"atan(\"].style.display = \"grid\";\r\n\r\n    }\r\n\r\n  };\r\n\r\n  resize = function(event) {\r\n\r\n    let height = Math.floor(document.documentElement.clientHeight);\r\n    let width = Math.floor(document.documentElement.clientWidth);\r\n    ui.screen.style.maxWidth = ui.screen.clientWidth + \"px\";\r\n\r\n  };\r\n\r\n  update = function() {\r\n\r\n    if (controller.active) {\r\n\r\n      controller.active = false;\r\n\r\n      switch(controller.value) {\r\n\r\n        case \"0\": case \"1\": case \"2\": case \"3\": case \"4\": case \"5\": case \"6\": case \"7\": case \"8\": case \"9\":\r\n        case \"+\": case \"-\": case \"/\": case \"*\": case \"(\": case \")\":\r\n        case \"PI\": case \"pow(\": case \"sqrt(\": case \"ln(\": case \"log(\": case \"rnd()\":\r\n        case \"cos(\": case \"sin(\": case \"tan(\": case \"acos(\": case \"asin(\": case \"atan(\":\r\n        case \",\": case \".\":\r\n\r\n          ui.screen.innerHTML += controller.value;\r\n\r\n        break;\r\n\r\n        case \"clr\": ui.screen.innerHTML = \"\"; break;\r\n        case \"f1\": ui.hitF1(); break;\r\n        case \"f2\": ui.hitF2(); break;\r\n        case \"f3\": ui.hitF3(); break;\r\n\r\n        case \"del\": case \"Delete\":\r\n\r\n          if (ui.screen.innerHTML.length > 0) ui.screen.innerHTML = ui.screen.innerHTML.slice(0, ui.screen.innerHTML.length - 1);\r\n\r\n        break;\r\n\r\n        case \"Enter\": case \"ans\":\r\n\r\n          let answer = undefined;\r\n\r\n          try {\r\n\r\n            answer = parseFloat(eval(ui.screen.innerHTML).toPrecision(10));\r\n\r\n          } catch(error) {\r\n\r\n            answer = error;\r\n\r\n          }\r\n\r\n          ui.screen.innerHTML = answer;\r\n\r\n        break;\r\n\r\n        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.\";\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  window.addEventListener(\"resize\", resize);\r\n\r\n  for (let property in ui.buttons) {\r\n\r\n    ui.buttons[property].addEventListener(\"click\",  controller.click, { passive:true });\r\n\r\n  }\r\n\r\n  window.addEventListener(\"keypress\", controller.keyPress);\r\n\r\n  resize();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/canvas/canvas.css",
    "content": "/* 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  width:100%;\r\n  height:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202430;\r\n  color:#ffffff;\r\n  display:grid;\r\n  font-family:monospace;\r\n  justify-items:center;\r\n  padding:8px;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n\r\n}\r\n"
  },
  {
    "path": "content/canvas/canvas.html",
    "content": "<!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 = \"canvas.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    <title>PoP Vlog - Canvas</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Canvas</h1>\r\n\r\n    <canvas id = \"display\"></canvas>\r\n\r\n    <script src = \"canvas.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/canvas/canvas.js",
    "content": "// Frank Poth 08/04/2017\r\n\r\nvar display = document.getElementById(\"display\").getContext(\"2d\");\r\n\r\ndisplay.canvas.height = 180;\r\ndisplay.canvas.width = 320;\r\n\r\ndisplay.fillStyle = \"#008000\";\r\ndisplay.fillRect(0, 0, 320, 180);\r\n\r\ndisplay.strokeStyle = \"#ffffff\";\r\ndisplay.lineJoin = \"round\";\r\ndisplay.lineWidth = 4;\r\n\r\ndisplay.fillStyle = \"#00ff00\";\r\ndisplay.beginPath();\r\ndisplay.moveTo(10, 10);\r\ndisplay.lineTo(10, 90);\r\ndisplay.lineTo(90, 10);\r\ndisplay.closePath();\r\ndisplay.fill();\r\ndisplay.stroke();\r\n\r\ndisplay.beginPath();\r\ndisplay.moveTo(0, 180);\r\ndisplay.bezierCurveTo(80, 0, 80, 180, 160, 90);\r\ndisplay.bezierCurveTo(240, 0, 240, 180, 320, 0);\r\ndisplay.stroke();\r\n\r\ndisplay.fillStyle = \"#0000ff\";\r\ndisplay.beginPath();\r\ndisplay.rect(180, 130, 40, 40);\r\ndisplay.fill();\r\ndisplay.stroke();\r\n\r\ndisplay.fillStyle = \"#ff0000\";\r\ndisplay.beginPath();\r\ndisplay.arc(290, 150, 20, 0, Math.PI*2);\r\ndisplay.fill();\r\ndisplay.stroke();\r\n"
  },
  {
    "path": "content/circle-collision-detection/circle-collision-detection.html",
    "content": "<!DOCTYPE html>\r\n\r\n<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision\r\ndetection. To determine if two circles are colliding you must check to see if the\r\ndistance between them is less than the sum of their radii. The formula for distance\r\nis: sqrt((x1 - x2)^2 + (y1 - y2)^2) So the check would look like:\r\n\r\nif (sqrt((x1 - x2)^2 + (y1 - y2)^2) <= radius1 + radius2) return true; // Collision\r\n\r\nSince sqrt is an expensive operation, we can instead check the squared distance\r\nagainst the squared sum of the radii:\r\n\r\nif ((x1 - x2)^2 + (y1 - y2)^2 <= (radius1 + radius2)^2) return true; // Collision\r\n\r\nAnd that's how you do circle collision. -->\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 = \"description\" content = \"Learn circle collision detection from pure JavaScript source code!\">\r\n\r\n    <title>Circle Collision Detection</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; box-sizing:border-box; }\r\n\r\n      html { height:100%; width:100%; }\r\n\r\n      body {\r\n\r\n        background-color:#202830;\r\n        display:grid;\r\n        grid-row-gap:16px;\r\n        grid-template-columns:100%;\r\n        grid-template-rows:min-content auto;\r\n        height:100%;\r\n        justify-items:center;\r\n        padding:16px;\r\n        width:100%;\r\n\r\n      }\r\n\r\n      p { color:#ffffff; font-size:1.25em; max-width:680px; }\r\n\r\n      a { color:#f08000; cursor:pointer; display:inline-block; font-weight:600; user-select:none; }\r\n\r\n      canvas { height:100%; max-width:680px; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <p>Two circles collide when the distance between their center points is less\r\n    than or equal to the sum of their radii. In this example, the distance between\r\n    circles is represented by the white line. Its length is <span id = \"length\" style = \"display:inline-block;\">0</span>.\r\n    the sum of the radii is <span id = \"sum_radii\" style = \"display:inline-block;\">0</span>. <a>change radii</a></p>\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n    // Frank Poth 04/15/2018\r\n\r\n      /* The Circle class defines a simple circle with its center point at x, y. */\r\n      const Circle = function(x, y, radius) {\r\n\r\n        this.x      = x;\r\n        this.y      = y;\r\n        this.radius = radius;\r\n\r\n      };\r\n\r\n      /* Fires when you click the \"change radii\" <a> tag. */\r\n      function changeRadii(event) {\r\n\r\n        circle1.radius = Math.floor(Math.random() * 41 + 10);\r\n        circle2.radius = Math.floor(Math.random() * 41 + 10);\r\n\r\n        update();\r\n\r\n      };\r\n\r\n      /* To test if two circles are overlapping, you simply test to see if the distance\r\n      between their two center points is less than their combined radii. The formula\r\n      for distance between two points is sqrt((p1.x - p2.x)^2 + (p1.y - p2.y)^2), but\r\n      since sqrt is an expensive operation, we can simply stop after we square the two\r\n      differences and test against the square of the sum of the radii. */\r\n      function collideCircle(circle1, circle2) {\r\n\r\n        /* first we get the x and y distance between the two circles. */\r\n        let distance_x = circle1.x      - circle2.x;\r\n        let distance_y = circle1.y      - circle2.y;\r\n        /* Then we get the sum of their radii. */\r\n        let radii_sum  = circle1.radius + circle2.radius;\r\n\r\n        /* Then we test to see if the square of their distance is greater than the\r\n        square of their radii. If it is, then there is no collision. If it isn't,\r\n        then we have a collision. */\r\n        if (distance_x * distance_x + distance_y * distance_y <= radii_sum * radii_sum) return true;\r\n\r\n        return false;\r\n\r\n      }\r\n\r\n      /* Fills the specified circle with the specified color. */\r\n      function fillCircle(circle, color) {\r\n\r\n        context.fillStyle = color;\r\n        context.beginPath();\r\n        //arc(centerX, centerY, radius, start_radian, stop_radian);\r\n        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);\r\n        context.fill();\r\n\r\n      }\r\n\r\n      /* Draws the boundary of the specified circle with the specified color. */\r\n      function strokeCircle(circle, color) {\r\n\r\n        context.strokeStyle = color;\r\n        context.beginPath();\r\n        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);\r\n        context.stroke();\r\n\r\n      }\r\n\r\n      /* Called whenever user input is entered via mouse or touch. */\r\n      function handleInput(event) {\r\n\r\n        /* Calling event.preventDefault is important so we don't scroll or scale\r\n        when we interact with the canvas. We can call it because the event listener's\r\n        passive property is set to false. */\r\n        event.preventDefault();\r\n\r\n        /* We need to know where the canvas is located in the browser window to\r\n        get the offset position to subtract from the input location. */\r\n        let canvas_rectangle = context.canvas.getBoundingClientRect();\r\n        let x = undefined;\r\n        let y = undefined;\r\n\r\n        switch(event.type) {\r\n\r\n          /* For any touch event, we must get the client position from the targetTouches array. */\r\n          case \"touchend\": case \"touchmove\": case \"touchstart\":\r\n\r\n          x = event.targetTouches[0].clientX;\r\n          y = event.targetTouches[0].clientY;\r\n\r\n          break;\r\n          /* The default would be mouse events, and these just use clientX and clientY. */\r\n          default:\r\n\r\n          x = event.clientX;\r\n          y = event.clientY;\r\n\r\n        }\r\n\r\n        /* Set circle1's position equal to the input position offset by the bounding rect of the canvas. */\r\n        circle1.x = x - canvas_rectangle.left;\r\n        circle1.y = y - canvas_rectangle.top;\r\n\r\n        update();// draw everything to the screen\r\n\r\n      }\r\n\r\n      /* Here we draw everything and we also test for collision with the collideCircle method. */\r\n      function update() {\r\n\r\n        /* Keep circle2 in the center of the canvas. */\r\n        circle2.x = context.canvas.clientWidth  * 0.5;\r\n        circle2.y = context.canvas.clientHeight * 0.5;\r\n\r\n        let collision = false;\r\n\r\n        if (collideCircle(circle1, circle2)) {// collision!\r\n\r\n          collision = true;\r\n\r\n        }\r\n\r\n        /* Set the output values. */\r\n        length.innerHTML = Math.round(Math.sqrt(Math.pow(circle1.x - circle2.x, 2) + Math.pow(circle1.y - circle2.y, 2)));\r\n        length.style.width = length.clientWidth + \"px\";\r\n        sum_radii.innerHTML = circle1.radius + circle2.radius;\r\n        sum_radii.style.width = sum_radii.clientWidth + \"px\";\r\n\r\n        /* We have to reset the canvas height and width in case the p element\r\n        gets resized. If it does the css will adjust the canvas height and skew\r\n        the image inside. To combat this, we just get the computed width and height\r\n        on each update. */\r\n        context.canvas.height = context.canvas.clientHeight;\r\n        context.canvas.width = context.canvas.clientWidth;\r\n\r\n        /* Clear the canvas to gray. */\r\n        context.fillStyle = \"#283038\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        if (collision) {\r\n\r\n          strokeCircle(circle1, \"#ffffff\");\r\n          strokeCircle(circle2, \"#ffffff\");\r\n\r\n        } else {\r\n\r\n          fillCircle(circle1, \"#0080f0\");\r\n          fillCircle(circle2, \"#f08000\");\r\n\r\n        }\r\n\r\n        /* Draw the white line. */\r\n        context.strokeStyle = \"#ffffff\";\r\n        context.beginPath();\r\n        context.moveTo(circle1.x, circle1.y);\r\n        context.lineTo(circle2.x, circle2.y);\r\n        context.stroke();\r\n\r\n      };\r\n\r\n      var circle1, circle2, context, length, sum_radii;\r\n\r\n      circle1   = new Circle(100, 100, 50);\r\n      circle2   = new Circle(100, 100, 50);\r\n      context   = document.querySelector(\"canvas\").getContext(\"2d\");\r\n      length    = document.getElementById(\"length\");\r\n      sum_radii = document.getElementById(\"sum_radii\");\r\n\r\n      window.addEventListener(\"resize\",             update);\r\n\r\n      /* We add all the input events to the canvas and use the same event handler.\r\n      passive:false is used to indicate that this input will override the normal\r\n      mouse and touch event actions that the browser might recognize as a scroll\r\n      or zoom command. */\r\n      context.canvas.addEventListener(\"mousedown\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"mousemove\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"mouseup\",    handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchend\",   handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchmove\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchstart\", handleInput, { passive:false });\r\n\r\n      document.querySelector(\"a\").addEventListener(\"click\", changeRadii);\r\n\r\n      update();\r\n      update();\r\n\r\n      /* We call update twice because the output can't be calculated until the canvas is\r\n      resized, but since the output changing size can cause the canvas to resize we\r\n      have to call it twice. */\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/circle-collision-response/circle-collision-response.html",
    "content": "<!DOCTYPE html>\r\n\r\n<!-- Frank Poth 04/15/2018 This example program showcases circle vs circle Collision\r\nresponse. -->\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 = \"description\" content = \"Learn circle collision response from pure JavaScript source code!\">\r\n\r\n    <title>Circle Collision Response</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; box-sizing:border-box; }\r\n\r\n      html { height:100%; width:100%; }\r\n\r\n      body {\r\n\r\n        background-color:#202830;\r\n        display:grid;\r\n        grid-row-gap:16px;\r\n        grid-template-columns:100%;\r\n        grid-template-rows:min-content auto;\r\n        height:100%;\r\n        justify-items:center;\r\n        padding:16px;\r\n        width:100%;\r\n\r\n      }\r\n\r\n      p { color:#ffffff; font-size:1.25em; max-width:680px; }\r\n\r\n      a { color:#f08000; cursor:pointer; display:inline-block; font-weight:600; user-select:none; }\r\n\r\n      canvas { height:100%; max-width:680px; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <p>To resolve collision between two circles, one of the circles must be \"pushed\"\r\n    out of the other until the distance between their center points is greater than\r\n    the sum of their radii. The circle should be moved out of collision along the\r\n    vector between the center points of the two circles. Its length is <span id = \"length\" style = \"display:inline-block;\">0</span>.\r\n    the sum of the radii is <span id = \"sum_radii\" style = \"display:inline-block;\">0</span>. <a>change radii</a></p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n    // Frank Poth 04/16/2018\r\n\r\n      /* The Circle class defines a simple circle with its center point at x, y. */\r\n      const Circle = function(x, y, radius) {\r\n\r\n        this.x      = x;\r\n        this.y      = y;\r\n        this.radius = radius;\r\n\r\n      };\r\n\r\n      /* Fires when you click the \"change radii\" <a> tag. */\r\n      function changeRadii(event) {\r\n\r\n        circle1.radius = Math.floor(Math.random() * 41 + 10);\r\n        circle2.radius = Math.floor(Math.random() * 41 + 10);\r\n\r\n        update();\r\n\r\n      };\r\n\r\n      /* To test if two circles are overlapping, you simply test to see if the distance\r\n      between their two center points is less than their combined radii. The formula\r\n      for distance between two points is sqrt((p1.x - p2.x)^2 + (p1.y - p2.y)^2), but\r\n      since sqrt is an expensive operation, we can simply stop after we square the two\r\n      differences and test against the square of the sum of the radii. */\r\n      function collideCircle(circle1, circle2) {\r\n\r\n        /* first we get the x and y distance between the two circles. */\r\n        let distance_x = circle1.x      - circle2.x;\r\n        let distance_y = circle1.y      - circle2.y;\r\n        /* Then we get the sum of their radii. */\r\n        let radii_sum  = circle1.radius + circle2.radius;\r\n\r\n        /* Then we test to see if the square of their distance is greater than the\r\n        square of their radii. If it is, then there is no collision. If it isn't,\r\n        then we have a collision. */\r\n        if (distance_x * distance_x + distance_y * distance_y <= radii_sum * radii_sum) return true;\r\n\r\n        return false;\r\n\r\n      }\r\n\r\n      function resolveCircle(c1, c2) {\r\n\r\n        let distance_x = c1.x      - c2.x;\r\n        let distance_y = c1.y      - c2.y;\r\n        let radii_sum  = c1.radius + c2.radius;\r\n        let length = Math.sqrt(distance_x * distance_x + distance_y * distance_y) || 1;\r\n        let unit_x = distance_x / length;\r\n        let unit_y = distance_y / length;\r\n\r\n        c1.x = c2.x + (radii_sum + 1) * unit_x;\r\n        c1.y = c2.y + (radii_sum + 1) * unit_y;\r\n\r\n      }\r\n\r\n      /* Fills the specified circle with the specified color. */\r\n      function fillCircle(circle, color) {\r\n\r\n        context.fillStyle = color;\r\n        context.beginPath();\r\n        //arc(centerX, centerY, radius, start_radian, stop_radian);\r\n        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);\r\n        context.fill();\r\n\r\n      }\r\n\r\n      /* Draws the boundary of the specified circle with the specified color. */\r\n      function strokeCircle(circle, color) {\r\n\r\n        context.strokeStyle = color;\r\n        context.beginPath();\r\n        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);\r\n        context.stroke();\r\n\r\n      }\r\n\r\n      /* Called whenever user input is entered via mouse or touch. */\r\n      function handleInput(event) {\r\n\r\n        /* Calling event.preventDefault is important so we don't scroll or scale\r\n        when we interact with the canvas. We can call it because the event listener's\r\n        passive property is set to false. */\r\n        event.preventDefault();\r\n\r\n        /* We need to know where the canvas is located in the browser window to\r\n        get the offset position to subtract from the input location. */\r\n        let canvas_rectangle = context.canvas.getBoundingClientRect();\r\n        let x = undefined;\r\n        let y = undefined;\r\n\r\n        switch(event.type) {\r\n\r\n          /* For any touch event, we must get the client position from the targetTouches array. */\r\n          case \"touchend\": case \"touchmove\": case \"touchstart\":\r\n\r\n          x = event.targetTouches[0].clientX;\r\n          y = event.targetTouches[0].clientY;\r\n\r\n          break;\r\n          /* The default would be mouse events, and these just use clientX and clientY. */\r\n          default:\r\n\r\n          x = event.clientX;\r\n          y = event.clientY;\r\n\r\n        }\r\n\r\n        /* Set circle1's position equal to the input position offset by the bounding rect of the canvas. */\r\n        circle1.x = x - canvas_rectangle.left;\r\n        circle1.y = y - canvas_rectangle.top;\r\n\r\n        update();// draw everything to the screen\r\n\r\n      }\r\n\r\n      /* Here we draw everything and we also test for collision with the collideCircle method. */\r\n      function update() {\r\n\r\n        /* Keep circle2 in the center of the canvas. */\r\n        circle2.x = context.canvas.clientWidth  * 0.5;\r\n        circle2.y = context.canvas.clientHeight * 0.5;\r\n\r\n        let collision = false;\r\n\r\n        if (collideCircle(circle1, circle2)) {// collision!\r\n\r\n          resolveCircle(circle1, circle2);// Response!\r\n\r\n          collision = true;\r\n\r\n        }\r\n\r\n        /* Set the output values. */\r\n        length.innerHTML = Math.round(Math.sqrt(Math.pow(circle1.x - circle2.x, 2) + Math.pow(circle1.y - circle2.y, 2)));\r\n        length.style.width = length.clientWidth + \"px\";\r\n        sum_radii.innerHTML = circle1.radius + circle2.radius;\r\n        sum_radii.style.width = sum_radii.clientWidth + \"px\";\r\n\r\n        /* We have to reset the canvas height and width in case the p element\r\n        gets resized. If it does the css will adjust the canvas height and skew\r\n        the image inside. To combat this, we just get the computed width and height\r\n        on each update. */\r\n        context.canvas.height = context.canvas.clientHeight;\r\n        context.canvas.width = context.canvas.clientWidth;\r\n\r\n        /* Clear the canvas to gray. */\r\n        context.fillStyle = \"#283038\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        if (collision) {\r\n\r\n          strokeCircle(circle1, \"#ffffff\");\r\n          strokeCircle(circle2, \"#ffffff\");\r\n\r\n        } else {\r\n\r\n          fillCircle(circle1, \"#0080f0\");\r\n          fillCircle(circle2, \"#f08000\");\r\n\r\n        }\r\n\r\n        /* Draw the white line. */\r\n        context.strokeStyle = \"#ffffff\";\r\n        context.beginPath();\r\n        context.moveTo(circle1.x, circle1.y);\r\n        context.lineTo(circle2.x, circle2.y);\r\n        context.stroke();\r\n\r\n      };\r\n\r\n      var circle1, circle2, context, length, sum_radii;\r\n\r\n      circle1   = new Circle(100, 100, 50);\r\n      circle2   = new Circle(100, 100, 50);\r\n      context   = document.querySelector(\"canvas\").getContext(\"2d\");\r\n      length    = document.getElementById(\"length\");\r\n      sum_radii = document.getElementById(\"sum_radii\");\r\n\r\n      /* We add all the input events to the canvas and use the same event handler.\r\n      passive:false is used to indicate that this input will override the normal\r\n      mouse and touch event actions that the browser might recognize as a scroll\r\n      or zoom command. */\r\n      context.canvas.addEventListener(\"mousedown\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"mousemove\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"mouseup\",    handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchend\",   handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchmove\",  handleInput, { passive:false });\r\n      context.canvas.addEventListener(\"touchstart\", handleInput, { passive:false });\r\n\r\n      window.addEventListener(\"resize\", update);\r\n\r\n      document.querySelector(\"a\").addEventListener(\"click\", changeRadii);\r\n\r\n      update();\r\n      update();// The second call is used to position the yellow circle the right way.\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/collision/collision.css",
    "content": "/* 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  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-items:center;\r\n\r\n}\r\n\r\nh1 {\r\n\r\n  word-wrap:break-word;\r\n\r\n}\r\n"
  },
  {
    "path": "content/collision/collision.html",
    "content": "<!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=device-width\">\r\n    <link href = \"collision.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Square Collision</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"collision.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/collision/collision.js",
    "content": "// Frank Poth 08/29/2017\r\n\r\nvar context, controller, Rectangle, red, white, loop, resize;\r\n\r\ncontext = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\ncontroller = {\r\n\r\n  // mouse or finger position\r\n  pointer_x:0,\r\n  pointer_y:0,\r\n\r\n  move:function(event) {\r\n\r\n    // This will give us the location of our canvas element\r\n    var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n    // store the position of the move event inside the pointer variables\r\n    controller.pointer_x = event.clientX - rectangle.left;\r\n    controller.pointer_y = event.clientY - rectangle.top;\r\n\r\n  }\r\n\r\n};\r\n\r\nRectangle = function(x, y, width, height, color) {\r\n\r\n  this.x = x;\r\n  this.y = y;\r\n  this.width = width;\r\n  this.height = height;\r\n\r\n  this.color = color;\r\n\r\n};\r\n\r\nRectangle.prototype = {\r\n\r\n  draw:function() {// draws rectangle to canvas\r\n\r\n    context.beginPath();\r\n    context.rect(this.x, this.y, this.width, this.height);\r\n    context.fillStyle = this.color;\r\n    context.fill();\r\n\r\n  },\r\n\r\n  // get the four side coordinates of the rectangle\r\n  get bottom() { return this.y + this.height; },\r\n  get left() { return this.x; },\r\n  get right() { return this.x + this.width; },\r\n  get top() { return this.y; },\r\n\r\n  testCollision:function(rectangle) {\r\n\r\n    if (this.top > rectangle.bottom || this.right < rectangle.left || this.bottom < rectangle.top || this.left > rectangle.right) {\r\n\r\n      return false;\r\n\r\n    }\r\n\r\n    return true;\r\n\r\n  }\r\n\r\n};\r\n\r\nred = new Rectangle(0, 0, 64, 64, \"#ff0000\");\r\nwhite = new Rectangle(context.canvas.width * 0.5 - 32, context.canvas.height * 0.5 - 32, 64, 64, \"#ffffff\");\r\n\r\nloop = function(time_stamp) {\r\n\r\n  red.x = controller.pointer_x - 32;\r\n  red.y = controller.pointer_y - 32;\r\n\r\n  context.fillStyle = \"#303840\";\r\n  context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n  white.draw();\r\n  red.draw();\r\n\r\n  if (red.testCollision(white)) {\r\n\r\n    context.beginPath();\r\n    context.rect(red.x, red.y, red.width, red.height);\r\n    context.rect(white.x, white.y, white.width, white.height);\r\n    context.strokeStyle = \"#ffffff\";\r\n    context.stroke();\r\n\r\n  }\r\n\r\n  window.requestAnimationFrame(loop);\r\n\r\n};\r\n\r\n// just keeps the canvas element sized appropriately\r\nresize = function(event) {\r\n\r\n  context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n  if (context.canvas.width > document.documentElement.clientHeight) {\r\n\r\n    context.canvas.width = document.documentElement.clientHeight;\r\n\r\n  }\r\n\r\n  context.canvas.height = Math.floor(context.canvas.width * 0.5625);\r\n\r\n  white.x = context.canvas.width * 0.5 - 32;\r\n  white.y = context.canvas.height * 0.5 - 32;\r\n\r\n};\r\n\r\ncontext.canvas.addEventListener(\"mousemove\", controller.move);\r\ncontext.canvas.addEventListener(\"touchmove\", controller.move, {passive:true});\r\n\r\nwindow.addEventListener(\"resize\", resize, {passive:true});\r\n\r\nresize();\r\n\r\n// start the game loop\r\nwindow.requestAnimationFrame(loop);\r\n"
  },
  {
    "path": "content/control/control.css",
    "content": "/* 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  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-content:center;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n\r\n}\r\n"
  },
  {
    "path": "content/control/control.html",
    "content": "<!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 Vlog - Control</title>\r\n    <link href = \"control.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Control</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>Use the keyboard to move the red square.</p>\r\n\r\n    <script src = \"control.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/control/control.js",
    "content": "// Frank Poth 08/13/2017\r\n\r\nvar context, controller, rectangle, loop;\r\n\r\ncontext = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\ncontext.canvas.height = 180;\r\ncontext.canvas.width = 320;\r\n\r\nrectangle = {\r\n\r\n  height:32,\r\n  jumping:true,\r\n  width:32,\r\n  x:144, // center of the canvas\r\n  x_velocity:0,\r\n  y:0,\r\n  y_velocity:0\r\n\r\n};\r\n\r\ncontroller = {\r\n\r\n  left:false,\r\n  right:false,\r\n  up:false,\r\n  keyListener:function(event) {\r\n\r\n    var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n    switch(event.keyCode) {\r\n\r\n      case 37:// left key\r\n        controller.left = key_state;\r\n      break;\r\n      case 38:// up key\r\n        controller.up = key_state;\r\n      break;\r\n      case 39:// right key\r\n        controller.right = key_state;\r\n      break;\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\nloop = function() {\r\n\r\n  if (controller.up && rectangle.jumping == false) {\r\n\r\n    rectangle.y_velocity -= 20;\r\n    rectangle.jumping = true;\r\n\r\n  }\r\n\r\n  if (controller.left) {\r\n\r\n    rectangle.x_velocity -= 0.5;\r\n\r\n  }\r\n\r\n  if (controller.right) {\r\n\r\n    rectangle.x_velocity += 0.5;\r\n\r\n  }\r\n\r\n  rectangle.y_velocity += 1.5;// gravity\r\n  rectangle.x += rectangle.x_velocity;\r\n  rectangle.y += rectangle.y_velocity;\r\n  rectangle.x_velocity *= 0.9;// friction\r\n  rectangle.y_velocity *= 0.9;// friction\r\n\r\n  // if rectangle is falling below floor line\r\n  if (rectangle.y > 180 - 16 - 32) {\r\n\r\n    rectangle.jumping = false;\r\n    rectangle.y = 180 - 16 - 32;\r\n    rectangle.y_velocity = 0;\r\n\r\n  }\r\n\r\n  // if rectangle is going off the left of the screen\r\n  if (rectangle.x < -32) {\r\n\r\n    rectangle.x = 320;\r\n\r\n  } else if (rectangle.x > 320) {// if rectangle goes past right boundary\r\n\r\n    rectangle.x = -32;\r\n\r\n  }\r\n\r\n  context.fillStyle = \"#202020\";\r\n  context.fillRect(0, 0, 320, 180);// x, y, width, height\r\n  context.fillStyle = \"#ff0000\";// hex for red\r\n  context.beginPath();\r\n  context.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);\r\n  context.fill();\r\n  context.strokeStyle = \"#202830\";\r\n  context.lineWidth = 4;\r\n  context.beginPath();\r\n  context.moveTo(0, 164);\r\n  context.lineTo(320, 164);\r\n  context.stroke();\r\n\r\n  // call update when the browser is ready to draw again\r\n  window.requestAnimationFrame(loop);\r\n\r\n};\r\n\r\nwindow.addEventListener(\"keydown\", controller.keyListener)\r\nwindow.addEventListener(\"keyup\", controller.keyListener);\r\nwindow.requestAnimationFrame(loop);\r\n"
  },
  {
    "path": "content/cube/cube.html",
    "content": "<!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=device-width\">\r\n    <title>Cube</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      canvas { height:100%; position:fixed; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      const Point2D = function(x, y) { this.x = x; this.y = y; };\r\n\r\n      const Point3D = function(x, y, z) { this.x = x; this.y = y; this.z = z; };\r\n\r\n      const Cube = function(x, y, z, size) {\r\n\r\n        Point3D.call(this, x, y, z);\r\n\r\n        size *= 0.5;\r\n\r\n        this.vertices = [new Point3D(x - size, y - size, z - size),\r\n                         new Point3D(x + size, y - size, z - size),\r\n                         new Point3D(x + size, y + size, z - size),\r\n                         new Point3D(x - size, y + size, z - size),\r\n                         new Point3D(x - size, y - size, z + size),\r\n                         new Point3D(x + size, y - size, z + size),\r\n                         new Point3D(x + size, y + size, z + size),\r\n                         new Point3D(x - size, y + size, z + size)];\r\n\r\n        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]];\r\n\r\n      };\r\n\r\n      Cube.prototype = {\r\n\r\n        rotateX:function(radian) {\r\n\r\n          var cosine = Math.cos(radian);\r\n          var sine   = Math.sin(radian);\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let p = this.vertices[index];\r\n\r\n            let y = (p.y - this.y) * cosine - (p.z - this.z) * sine;\r\n            let z = (p.y - this.y) * sine + (p.z - this.z) * cosine;\r\n\r\n            p.y = y + this.y;\r\n            p.z = z + this.z;\r\n\r\n          }\r\n\r\n        },\r\n\r\n        rotateY:function(radian) {\r\n\r\n          var cosine = Math.cos(radian);\r\n          var sine   = Math.sin(radian);\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let p = this.vertices[index];\r\n\r\n            let x = (p.z - this.z) * sine + (p.x - this.x) * cosine;\r\n            let z = (p.z - this.z) * cosine - (p.x - this.x) * sine;\r\n\r\n            p.x = x + this.x;\r\n            p.z = z + this.z;\r\n\r\n          }\r\n\r\n        }\r\n\r\n      };\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n      var pointer = new Point2D(0, 0);\r\n      var cube = new Cube(0, 0, 400, 200);\r\n\r\n      var height = document.documentElement.clientHeight;\r\n      var width = document.documentElement.clientWidth;\r\n\r\n      function project(points3d, width, height) {\r\n\r\n        var points2d = new Array(points3d.length);\r\n\r\n        var focal_length = 200;\r\n\r\n        for (let index = points3d.length - 1; index > -1; -- index) {\r\n\r\n          let p = points3d[index];\r\n\r\n          let x = p.x * (focal_length / p.z) + width * 0.5;\r\n          let y = p.y * (focal_length / p.z) + height * 0.5;\r\n\r\n          points2d[index] = new Point2D(x, y);\r\n\r\n        }\r\n\r\n        return points2d;\r\n\r\n      }\r\n\r\n      function loop() {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        height = document.documentElement.clientHeight;\r\n        width = document.documentElement.clientWidth;\r\n\r\n        context.canvas.height = height;\r\n        context.canvas.width  = width;\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(0, 0, width, height);\r\n\r\n        context.strokeStyle = \"#ffffff\";\r\n\r\n        cube.rotateX(pointer.y * 0.0001);\r\n        cube.rotateY(-pointer.x * 0.0001);\r\n\r\n        context.fillStyle = \"#0080f0\";\r\n\r\n        var vertices = project(cube.vertices, width, height);\r\n\r\n        for (let index = cube.faces.length - 1; index > -1; -- index) {\r\n\r\n          let face = cube.faces[index];\r\n\r\n          let p1 = cube.vertices[face[0]];\r\n          let p2 = cube.vertices[face[1]];\r\n          let p3 = cube.vertices[face[2]];\r\n\r\n          let v1 = new Point3D(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);\r\n          let v2 = new Point3D(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z);\r\n\r\n          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);\r\n\r\n          if (-p1.x * n.x + -p1.y * n.y + -p1.z * n.z <= 0) {\r\n\r\n            context.beginPath();\r\n            context.moveTo(vertices[face[0]].x, vertices[face[0]].y);\r\n            context.lineTo(vertices[face[1]].x, vertices[face[1]].y);\r\n            context.lineTo(vertices[face[2]].x, vertices[face[2]].y);\r\n            context.lineTo(vertices[face[3]].x, vertices[face[3]].y);\r\n            context.closePath();\r\n            context.fill();\r\n            context.stroke();\r\n\r\n          }\r\n\r\n\r\n        }\r\n\r\n      }\r\n\r\n      loop();\r\n\r\n      window.addEventListener(\"click\", (event) => {\r\n\r\n        pointer.x = event.pageX - width * 0.5;\r\n        pointer.y = event.pageY - height * 0.5;\r\n\r\n      });\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/dino/dino.css",
    "content": "/* 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\nhtml {\r\n\r\n  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto;\r\n  height:100%;\r\n  justify-items:center;\r\n  padding:0 8px;\r\n  width:100%;\r\n\r\n}\r\n"
  },
  {
    "path": "content/dino/dino.html",
    "content": "<!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 = \"description\" content = \"A simple game in which a dinosaur jumps over stuff based on Chrome's endless runner.\">\r\n\r\n    <link href = \"dino.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Dino Run</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>Click or tap to jump!</p>\r\n\r\n    <script src = \"dino.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/dino/dino.js",
    "content": "// 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 rightmost column is randomly generated when scrolling. There is animation.\r\nThere is collision detection between all moving objects and the world as well as\r\nthe player and the meteors and tarpits. There is an effect that turns the screen\r\nred when a meteor spawns using image data. I implement object pooling to avoid using\r\n\"new\" to create new objects. Some of this stuff I've covered in old tutorials, and\r\nsome stuff I have not covered. */\r\n\r\n(function() { \"use strict\";\r\n\r\n  const TILE_SIZE = 16;\r\n  const WORLD_HEIGHT = 144;\r\n  const WORLD_WIDTH = 256;\r\n\r\n  //// CLASSES ////\r\n\r\n  var Animation = function(frame_set, delay) {\r\n\r\n    this.count = 0;// Counts the number of game cycles since the last frame change.\r\n    this.delay = delay;// The number of game cycles to wait until the next frame change.\r\n    this.frame_value = frame_set[0];// The value in the sprite sheet of the sprite image / tile to display.\r\n    this.frame_index = 0;// The frame's index in the current animation frame set.\r\n    this.frame_set = frame_set;// The current animation frame set that holds sprite tile values.\r\n\r\n  };\r\n\r\n  Animation.prototype = {\r\n\r\n    /* This changes the current animation frame set. For example, if the current\r\n    set is [0, 1], and the new set is [2, 3], it changes the set to [2, 3]. It also\r\n    sets the delay. */\r\n    change:function(frame_set, delay = 15) {\r\n\r\n      if (this.frame_set != frame_set) {// If the frame set is different:\r\n\r\n        this.count = 0;// Reset the count.\r\n        this.delay = delay;// Set the delay.\r\n        this.frame_index = 0;// Start at the first frame in the new frame set.\r\n        this.frame_set = frame_set;// Set the new frame set.\r\n        this.frame_value = this.frame_set[this.frame_index];// Set the new frame value.\r\n\r\n      }\r\n\r\n    },\r\n\r\n    /* Call this on each game cycle. */\r\n    update:function() {\r\n\r\n      this.count ++;// Keep track of how many cycles have passed since the last frame change.\r\n\r\n      if (this.count >= this.delay) {// If enough cycles have passed, we change the frame.\r\n\r\n        this.count = 0;// Reset the count.\r\n        /* If the frame index is on the last value in the frame set, reset to 0.\r\n        If the frame index is not on the last value, just add 1 to it. */\r\n        this.frame_index = (this.frame_index == this.frame_set.length - 1) ? 0 : this.frame_index + 1;\r\n        this.frame_value = this.frame_set[this.frame_index];// Change the current frame value.\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* A frame just keeps track of a physical position inside the tile sheet for blitting. */\r\n  var Frame = function(x, y, width, height) {\r\n\r\n    this.height = height;\r\n    this.width  = width;\r\n    this.x      = x;\r\n    this.y      = y;\r\n\r\n  };\r\n\r\n  /* A Pool object manages objects. The objects array holds all objects that are\r\n  currently in use, and the pool holds objects that are not in use. By storing objects\r\n  that would otherwise be deleted, we can reuse them instead of creating totally new\r\n  instances with the new operator. Recycling saves memory. Do it. */\r\n  var Pool = function(object) {\r\n\r\n    this.object = object;// The constructor of the object we are pooling.\r\n    this.objects = [];// The array of objects in use.\r\n    this.pool = [];// The array of objects not in use.\r\n\r\n  };\r\n\r\n  Pool.prototype = {\r\n\r\n    /* Get an object from the pool or create a new object. Pool expects objects to\r\n    have a few basic functions, like reset. */\r\n    get:function(parameters) {\r\n\r\n      if (this.pool.length != 0) {\r\n\r\n        let object = this.pool.pop();\r\n        object.reset(parameters);\r\n        this.objects.push(object);\r\n\r\n      } else {\r\n\r\n        this.objects.push(new this.object(parameters.x, parameters.y));\r\n\r\n      }\r\n\r\n    },\r\n\r\n    store:function(object) {\r\n\r\n      let index = this.objects.indexOf(object);\r\n\r\n      if (index != -1) {\r\n\r\n        this.pool.push(this.objects.splice(index, 1)[0]);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    storeAll:function() {\r\n\r\n      for (let index = this.objects.length - 1; index > -1; -- index) {\r\n\r\n        this.pool.push(this.objects.pop());\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var Meteor = function(x, y) {\r\n\r\n    this.alive = true;// Meteor dies when it goes offscreen.\r\n    this.animation = new Animation(display.tile_sheet.frame_sets[1], 8);\r\n    this.grounded = false;\r\n    this.smoke = false;// smoke values are used for spawning smoke particles.\r\n    this.smoke_count = 0;\r\n    this.smoke_delay = Math.floor(Math.random() * 10 + 5);\r\n    this.height = Math.floor(Math.random() * 16 + 24); this.width = this.height;\r\n    this.x = x; this.y = y - this.height * 0.5;\r\n    let direction = Math.PI * 1.75 + Math.random() * Math.PI * 0.1;// The trajectory.\r\n    this.x_velocity = Math.cos(direction) * 3; this.y_velocity = -Math.sin(direction) * 3;\r\n\r\n  };\r\n\r\n  /* All game objects are expected to have collideWorld and CollideObject functions,\r\n  as well as update and reset functions. If this were a strongly typed language, I\r\n  would be using a base class called GameObject or something. */\r\n  Meteor.prototype = {\r\n\r\n    constructor:Meteor,\r\n\r\n    collideObject:function(player) {\r\n\r\n      let vector_x = player.x + player.width * 0.5 - this.x - this.width * 0.5;\r\n      let vector_y = player.y + player.height * 0.5 - this.y - this.height * 0.5;\r\n      let combined_radius = player.height * 0.5 + this.width * 0.5;\r\n\r\n      if (vector_x * vector_x + vector_y * vector_y < combined_radius * combined_radius) {\r\n\r\n        player.alive = false;\r\n        player.animation.change(display.tile_sheet.frame_sets[5], 10);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    collideWorld:function() {\r\n\r\n      if (this.x + this.width < 0) {\r\n\r\n        this.alive = false;\r\n        return;\r\n\r\n      }\r\n\r\n      if (this.y + this.height > WORLD_HEIGHT - 6) {\r\n\r\n        this.x_velocity = -game.speed;\r\n        this.grounded = true;\r\n        this.y = WORLD_HEIGHT - this.height - 6;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    reset:function(parameters) {\r\n\r\n      this.alive = true;\r\n      this.animation.change(display.tile_sheet.frame_sets[1], 8);\r\n      this.grounded = false;\r\n      this.x = parameters.x;\r\n      let direction = Math.PI * 1.75 + Math.random() * Math.PI * 0.1;\r\n      this.x_velocity = Math.cos(direction) * 3;\r\n      this.y = parameters.y;\r\n      this.y_velocity = -Math.sin(direction) * 3;\r\n\r\n    },\r\n\r\n    update:function() {\r\n\r\n      if (!this.grounded) {\r\n\r\n        this.animation.update();\r\n        this.y += this.y_velocity;\r\n\r\n      } else {\r\n\r\n        this.x_velocity = -game.speed;\r\n\r\n      }\r\n\r\n      this.x += this.x_velocity;\r\n\r\n      this.smoke_count ++;\r\n      if (this.smoke_count == this.smoke_delay) {\r\n\r\n        this.smoke_count = 0;\r\n        this.smoke = true;\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var Smoke = function(x, y, x_velocity, y_velocity) {\r\n\r\n    this.alive = true;\r\n    this.animation = new Animation(display.tile_sheet.frame_sets[2], 8);\r\n    this.life_count = 0;\r\n    this.life_time = Math.random() * 20 + 30;\r\n    this.height = 8 + Math.floor(Math.random() * 8); this.width = this.height;\r\n    this.x = x; this.y = y;\r\n    this.x_velocity = x_velocity; this.y_velocity = y_velocity;\r\n\r\n  };\r\n\r\n  Smoke.prototype = {\r\n\r\n    constructor:Smoke,\r\n\r\n    collideWorld:function() {\r\n\r\n      if (this.x > WORLD_WIDTH || this.y > WORLD_HEIGHT - 20) {\r\n\r\n        this.alive = false;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    reset:function(parameters) {\r\n\r\n      this.alive = true;\r\n      this.life_count = 0;\r\n      this.life_time = Math.random() * 20 + 30;\r\n      this.x          = parameters.x;\r\n      this.x_velocity = parameters.x_velocity;\r\n      this.y          = parameters.y;\r\n      this.y_velocity = parameters.y_velocity;\r\n\r\n    },\r\n\r\n    update:function() {\r\n\r\n      this.animation.update();\r\n      this.x += this.x_velocity;\r\n      this.y += this.y_velocity;\r\n\r\n      this.life_count ++;\r\n\r\n      if (this.life_count > this.life_time) {\r\n\r\n        this.alive = false;\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var TarPit = function(x, y) {\r\n\r\n    this.alive = true;\r\n    this.animation = new Animation(display.tile_sheet.frame_sets[0], 8);\r\n    this.height = 30; this.width = Math.floor(Math.random() * 64 + 48);\r\n    this.x = x; this.y = y;\r\n\r\n  };\r\n\r\n  TarPit.prototype = {\r\n\r\n    constructor:TarPit,\r\n\r\n    collideObject:function(player) {\r\n\r\n    },\r\n\r\n    collideObject:function(object) {\r\n\r\n      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) {\r\n\r\n        object.alive = false;\r\n        object.animation.change(display.tile_sheet.frame_sets[4], 10);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    collideWorld:function() {\r\n\r\n      if (this.x + this.width < 0) this.alive = false;\r\n\r\n    },\r\n\r\n    reset:function(parameters) {\r\n\r\n      this.alive = true;\r\n      this.width = Math.floor(Math.random() * 64 + 48);\r\n      this.x = parameters.x;\r\n      this.y = parameters.y;\r\n\r\n    },\r\n\r\n    update:function(){\r\n\r\n      this.animation.update();\r\n      this.x -= game.speed;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var controller, display, game;\r\n\r\n  /* This is awesome. I can use the same event handler for all mouseup, mousedown,\r\n  touchstart, and touchend events. This controller works on everything! */\r\n  controller = {\r\n\r\n    active:false, state:false,\r\n\r\n    onOff:function(event) {\r\n\r\n      event.preventDefault();\r\n\r\n      let key_state = (event.type == \"mousedown\" || event.type == \"touchstart\") ? true : false;\r\n\r\n      if (controller.state != key_state) controller.active = key_state;\r\n      controller.state  = key_state;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n\r\n    tint:0,// The red tint value to add to the buffer's red channel when a meteor is on screen.\r\n\r\n    tile_sheet: {\r\n\r\n      columns:undefined,// Set up in INITIALIZE section.\r\n      frames: [new Frame( 0, 32, 24, 16), new Frame(24, 32, 24, 16),// tar pit\r\n               new Frame(64, 32, 16, 16), new Frame(80, 32, 16, 16),// Meteor\r\n               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\r\n               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\r\n               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\r\n               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\r\n              ],\r\n\r\n      frame_sets:[[ 0, 1],//tar pit\r\n                  [ 2, 3],//Meteor\r\n                  [ 4, 5, 6, 7],//smoke\r\n                  [ 8, 9,10,11,12,13,14,15],//dino run\r\n                  [16,17,18,19,20,21],//dino sink\r\n                  [22,23,24,25,26,27]//dino crisp\r\n\r\n      ],\r\n      image:new Image()// The tile sheet image is loaded at the bottom of this file.\r\n\r\n    },\r\n\r\n    render:function() {\r\n\r\n      // Draw Tiles\r\n      for (let index = game.area.map.length - 1; index > -1; -- index) {\r\n\r\n        let value = game.area.map[index];\r\n\r\n        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);\r\n\r\n      }\r\n\r\n      // Draw distance\r\n      this.buffer.font = \"20px Arial\";\r\n      this.buffer.fillStyle = \"#ffffff\";\r\n      this.buffer.fillText(String(Math.floor(game.distance/10) + \" / \" + Math.floor(game.max_distance/10)), 10, 20);\r\n\r\n      // Draw TarPits\r\n      for (let index = game.object_manager.tarpit_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n        let tarpit = game.object_manager.tarpit_pool.objects[index];\r\n\r\n        let frame = this.tile_sheet.frames[tarpit.animation.frame_value];\r\n\r\n        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, tarpit.x, tarpit.y, tarpit.width, tarpit.height);\r\n\r\n      }\r\n\r\n      // Draw Player\r\n      let frame = this.tile_sheet.frames[game.player.animation.frame_value];\r\n\r\n      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);\r\n\r\n      // Draw Meteors\r\n      for (let index = game.object_manager.meteor_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n        let meteor = game.object_manager.meteor_pool.objects[index];\r\n\r\n        let frame = this.tile_sheet.frames[meteor.animation.frame_value];\r\n\r\n        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, meteor.x, meteor.y, meteor.width, meteor.height);\r\n\r\n      }\r\n\r\n      // Draw Smoke\r\n      for (let index = game.object_manager.smoke_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n        let smoke = game.object_manager.smoke_pool.objects[index];\r\n\r\n        let frame = this.tile_sheet.frames[smoke.animation.frame_value];\r\n\r\n        this.buffer.drawImage(this.tile_sheet.image, frame.x, frame.y, frame.width, frame.height, smoke.x, smoke.y, smoke.width, smoke.height);\r\n\r\n      }\r\n\r\n      // Draw tint if a meteor is on screen\r\n      if (game.object_manager.meteor_pool.objects.length != 0) {\r\n\r\n        this.tint = (this.tint < 80) ? this.tint + 1 : 80;\r\n\r\n      } else {// Reduce tint otherwise\r\n\r\n        this.tint = (this.tint > 0) ? this.tint - 2 : 0;\r\n\r\n      }\r\n\r\n      if (this.tint != 0) {// If there is a tint to draw, apply it to the buffer\r\n\r\n        let image_data = this.buffer.getImageData(0, 0, WORLD_WIDTH, WORLD_HEIGHT);\r\n        let data = image_data.data;\r\n\r\n        for (let index = data.length - 4; index > -1; index -= 4) {\r\n\r\n          data[index] += this.tint;\r\n\r\n        }\r\n\r\n        this.buffer.putImageData(image_data, 0, 0);\r\n\r\n      }\r\n\r\n      this.context.drawImage(this.buffer.canvas, 0, 0, WORLD_WIDTH, WORLD_HEIGHT, 0, 0, this.context.canvas.width, this.context.canvas.height);\r\n\r\n    },\r\n\r\n    resize:function(event) {\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n      if (display.context.canvas.width > document.documentElement.clientHeight - 16) {\r\n\r\n        display.context.canvas.width = document.documentElement.clientHeight - 16;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = display.context.canvas.width * 0.5625;\r\n\r\n      display.buffer.imageSmoothingEnabled = false;\r\n      display.context.imageSmoothingEnabled = false;\r\n\r\n      display.render();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  game = {\r\n\r\n    distance:0,\r\n    max_distance:0,\r\n    speed:3,\r\n\r\n    area: {\r\n\r\n      columns:17,\r\n      offset:0,\r\n      map:[ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0,\r\n            0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1,\r\n            1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1,\r\n            1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,\r\n            0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0,\r\n            1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0,\r\n            1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0,\r\n            2, 2, 2, 3, 2, 2, 3, 2, 4, 6, 7, 7, 6, 9, 2, 3, 2,\r\n           10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10],\r\n\r\n      /* Takes care of scrolling the background and generating the next column to\r\n      display on the far right of the map. */\r\n      scroll:function() {\r\n\r\n        game.distance += game.speed;\r\n\r\n        if (game.distance > game.max_distance) game.max_distance = game.distance;\r\n\r\n        this.offset += game.speed;\r\n        if (this.offset >= TILE_SIZE) {\r\n\r\n          this.offset -= TILE_SIZE;\r\n\r\n          /* This loop removes the first column and inserts a randomly generated\r\n          last column for the top 7 rows. This handles random sky generation. */\r\n          for (let index = this.map.length - this.columns * 3 ; index > -1; index -= this.columns) {\r\n\r\n            this.map.splice(index, 1);\r\n            this.map.splice(index + this.columns - 1, 0, Math.floor(Math.random() * 2));\r\n\r\n          }\r\n\r\n          /* This next part replaces the grass with an appropriate grass tile. I\r\n          made it a bit more complex than it needed to be, but the tiles actually\r\n          reconcile their edges with the tile directly to the left. */\r\n          this.map.splice(this.columns * 7, 1);\r\n\r\n          let right_index = this.columns * 8 - 1;\r\n          let value = this.map[right_index - 1];\r\n\r\n          switch(value) {\r\n\r\n            case 2: case 3: value = [2, 3, 2, 3, 2, 3, 2, 3, 4, 5][Math.floor(Math.random() * 10)]; break;\r\n            case 4: case 5: value = [6, 7][Math.floor(Math.random() * 2)]; break;\r\n            case 6: case 7: value = [6, 7, 8, 9][Math.floor(Math.random() * 4)]; break;\r\n            case 8: case 9: value = [2, 3][Math.floor(Math.random() * 2)]; break;\r\n\r\n          }\r\n\r\n          this.map.splice(right_index, 0, value);\r\n\r\n          // The last row stays the same. It's just dirt.\r\n\r\n        }\r\n\r\n      }\r\n\r\n    },\r\n\r\n    engine: {\r\n\r\n      /* Fixed time step game loop!! */\r\n      afrequest:undefined,// animation frame request reference\r\n      accumulated_time:window.performance.now(),\r\n      time_step:1000/60,// update rate\r\n\r\n      loop:function(time_stamp) {\r\n\r\n        /* How easy does this look? This is a fixed step loop with frame dropping.\r\n        Amazingly it's super simple and only a few lines. This will make your game\r\n        run at the same speed on all devices. Now that I look at it, I think there\r\n        may be a better way to implement this because entire frames can be dropped\r\n        without updating or rendering. Rather than fixing this now, I will just leave it.\r\n        Ideally, I would utilize the free time and not do both updates and renderings\r\n        at the same time unless I have to... Another day... This does work fine, though. */\r\n        if (time_stamp >= game.engine.accumulated_time + game.engine.time_step) {\r\n\r\n          if (time_stamp - game.engine.accumulated_time >= game.engine.time_step * 4) {\r\n\r\n            game.engine.accumulated_time = time_stamp;\r\n\r\n          }\r\n\r\n          while(game.engine.accumulated_time < time_stamp) {\r\n\r\n            game.engine.accumulated_time += game.engine.time_step;\r\n\r\n            game.engine.update();\r\n\r\n          }\r\n\r\n          display.render();\r\n\r\n        }\r\n\r\n        window.requestAnimationFrame(game.engine.loop);\r\n\r\n      },\r\n\r\n      start:function() {// Start the game loop.\r\n\r\n        this.afrequest = window.requestAnimationFrame(this.loop);\r\n\r\n      },\r\n\r\n      update:function() {// Update the game logic.\r\n\r\n        /* Slowly increase speed and cap it when it gets too high. */\r\n        game.speed = (game.speed >= TILE_SIZE * 0.5)? TILE_SIZE * 0.5 : game.speed + 0.001;\r\n        /* Make sure the player's animation delay is keeping up with the scroll speed. */\r\n        game.player.animation.delay = Math.floor(10 - game.speed);\r\n        game.area.scroll();// Scroll!!!\r\n\r\n        if (game.player.alive) {\r\n\r\n          if (controller.active && !game.player.jumping) {// Get user input\r\n\r\n            controller.active = false;\r\n            game.player.jumping = true;\r\n            game.player.y_velocity -= 15;\r\n            game.player.animation.change([10], 15);\r\n\r\n          }\r\n\r\n          if (game.player.jumping == false) {\r\n\r\n            game.player.animation.change(display.tile_sheet.frame_sets[3], Math.floor(TILE_SIZE - game.speed));\r\n\r\n          }\r\n\r\n          game.player.update();\r\n\r\n          if (game.player.y > TILE_SIZE * 6 - TILE_SIZE * 0.25) {// Collide with floor\r\n\r\n            controller.active = false;\r\n            game.player.y = TILE_SIZE * 6 - TILE_SIZE * 0.25;\r\n            game.player.y_velocity = 0;\r\n            game.player.jumping = false;\r\n\r\n          }\r\n\r\n        } else {\r\n\r\n          game.player.x -= game.speed;\r\n          game.speed *= 0.9;\r\n\r\n          if (game.player.animation.frame_index == game.player.animation.frame_set.length - 1) game.reset();\r\n\r\n        }\r\n\r\n        game.player.animation.update();\r\n\r\n        game.object_manager.spawn();\r\n        game.object_manager.update();\r\n\r\n      }\r\n\r\n    },\r\n\r\n    /* Manages all non player objects. */\r\n    object_manager: {\r\n\r\n      count:0,\r\n      delay:100,\r\n\r\n      meteor_pool:new Pool(Meteor),\r\n      smoke_pool:new Pool(Smoke),\r\n      tarpit_pool:new Pool(TarPit),\r\n\r\n      spawn:function() {\r\n\r\n        this.count ++;\r\n\r\n        if (this.count == this.delay) {\r\n\r\n          this.count = 0;\r\n          this.delay = 100;// + Math.floor(Math.random() * 200 - 10 * game.speed);\r\n\r\n          /* Pick randomly between tarpits and meteors */\r\n          if (Math.random() > 0.5) {\r\n\r\n            this.tarpit_pool.get( {x: WORLD_WIDTH, y:WORLD_HEIGHT - 30} );\r\n\r\n          } else {\r\n\r\n            this.meteor_pool.get( {x: WORLD_WIDTH * 0.2, y: -32 } );\r\n\r\n          }\r\n\r\n        }\r\n\r\n      },\r\n\r\n      update:function() {\r\n\r\n        for (let index = this.meteor_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n          let meteor = this.meteor_pool.objects[index];\r\n\r\n          meteor.update();\r\n\r\n          meteor.collideObject(game.player);\r\n\r\n          meteor.collideWorld();\r\n\r\n          if (meteor.smoke) {\r\n\r\n            meteor.smoke = false;\r\n\r\n            let parameters = { x:meteor.x + Math.random() * meteor.width, y:undefined, x_velocity:undefined, y_velocity:undefined };\r\n\r\n            if (meteor.grounded) {\r\n\r\n              parameters.y = meteor.y + Math.random() * meteor.height * 0.5;\r\n              parameters.x_velocity = Math.random() * 2 - 1 - game.speed;\r\n              parameters.y_velocity = Math.random() * -1;\r\n\r\n            } else {\r\n\r\n              parameters.y = meteor.y + Math.random() * meteor.height;\r\n              parameters.x_velocity = meteor.x_velocity * Math.random();\r\n              parameters.y_velocity = meteor.y_velocity * Math.random();\r\n\r\n            }\r\n\r\n            this.smoke_pool.get(parameters);\r\n\r\n          }\r\n\r\n          if (!meteor.alive) {\r\n\r\n            this.meteor_pool.store(meteor);\r\n\r\n          };\r\n\r\n        }\r\n\r\n        for (let index = this.smoke_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n          let smoke = this.smoke_pool.objects[index];\r\n\r\n          smoke.update();\r\n\r\n          smoke.collideWorld();\r\n\r\n          if (!smoke.alive) this.smoke_pool.store(smoke);\r\n\r\n        }\r\n\r\n        for (let index = this.tarpit_pool.objects.length - 1; index > -1; -- index) {\r\n\r\n          let tarpit = this.tarpit_pool.objects[index];\r\n\r\n          tarpit.update();\r\n\r\n          tarpit.collideObject(game.player);\r\n\r\n          tarpit.collideWorld();\r\n\r\n          if (!tarpit.alive) this.tarpit_pool.store(tarpit);\r\n\r\n        }\r\n\r\n      }\r\n\r\n    },\r\n\r\n    player: {\r\n\r\n      alive:true,\r\n      animation:new Animation([15], 10),\r\n      jumping:false,\r\n      height: 32, width: 56,\r\n      x:8, y:TILE_SIZE * 6 - TILE_SIZE * 0.25,\r\n      y_velocity:0,\r\n\r\n      reset:function() {\r\n\r\n        this.alive = true;\r\n        this.x = 8;\r\n\r\n      },\r\n\r\n      update:function() {\r\n\r\n        game.player.y_velocity += 0.5;\r\n        game.player.y += game.player.y_velocity;\r\n        game.player.y_velocity *= 0.9;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    reset:function() {\r\n\r\n      this.distance = 0;\r\n      this.player.reset();\r\n\r\n      /* Put all of our objects away. */\r\n      this.object_manager.meteor_pool.storeAll();\r\n      this.object_manager.smoke_pool.storeAll();\r\n      this.object_manager.tarpit_pool.storeAll();\r\n\r\n      this.speed = 3;\r\n\r\n    }\r\n\r\n  };\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.buffer.canvas.height = WORLD_HEIGHT;\r\n  display.buffer.canvas.width  = WORLD_WIDTH;\r\n\r\n  display.tile_sheet.image.src = \"dino.png\";\r\n  display.tile_sheet.image.addEventListener(\"load\", function(event) {\r\n\r\n    display.tile_sheet.columns = this.width / TILE_SIZE;\r\n\r\n    display.resize();\r\n\r\n    game.engine.start();\r\n\r\n  });\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  window.addEventListener(\"mousedown\", controller.onOff);\r\n  window.addEventListener(\"mouseup\", controller.onOff);\r\n  window.addEventListener(\"touchstart\", controller.onOff);\r\n  window.addEventListener(\"touchend\", controller.onOff);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/dominiques-doors/area0.json",
    "content": "{\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\n  \"doors\":[\r\n\r\n    { \"area\":\"area1.json\", \"x\":48, \"new_x\":112 }\r\n\r\n  ]\r\n\r\n}\r\n"
  },
  {
    "path": "content/dominiques-doors/area1.json",
    "content": "{\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\r\n  \"doors\":[\r\n\r\n    { \"area\":\"area2.json\", \"x\":48, \"new_x\":48 },\r\n    { \"area\":\"area0.json\", \"x\":112, \"new_x\":48 },\r\n    { \"area\":\"area3.json\", \"x\":176, \"new_x\":16 }\r\n\r\n  ]\r\n\r\n}\r\n"
  },
  {
    "path": "content/dominiques-doors/area2.json",
    "content": "{\r\n\r\n  \"message\":\"~The Giant Pointless Room~\",\r\n  \"background_color\":\"#006666\",\r\n  \"floor\":250,\r\n  \"height\":256,\r\n  \"width\":512,\r\n\r\n  \"doors\":[\r\n\r\n    { \"area\":\"area1.json\", \"x\":48, \"new_x\":48 }\r\n\r\n  ]\r\n\r\n}\r\n"
  },
  {
    "path": "content/dominiques-doors/area3.json",
    "content": "{\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  \"doors\":[\r\n\r\n    { \"area\":\"area1.json\", \"x\":16, \"new_x\":176 }\r\n\r\n  ]\r\n\r\n}\r\n"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.css",
    "content": "/* 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%; width:100%; }\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  align-items:space-around;\r\n  color:#ffffff;\r\n  background-color:#202830;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nh1 { font-size: 3.0em; text-align:center; }\r\n"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.html",
    "content": "<!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\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \"description\" content = \"Learn how to load JSON level data for a web game with JavaScript!\">\r\n\r\n    <title>PoP Vlog - Loading Levels!!!</title>\r\n\r\n  </head>          \r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Loading Levels!!!</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"dominiques-doors.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/dominiques-doors/dominiques-doors.js",
    "content": "// 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 sprites, and other basic game design techniques including: user Input\r\non the keyboard, some collision detection and response, image loading, and maybe\r\nsome other things, too. */\r\n\r\n/* You may notice that in the largest room the dominique sprite looks a little weird.\r\nThere is a brown bar that flashes in front of her face when she walks to the left.\r\nThis is because of something called \"texture bleeding\" where a scaled image allows\r\npixels from around the cropped source region of the image to bleed into the desired\r\npart of the source image. This is okay for cropping from large images, but for sprite\r\nsheets it's not desireable. A way around this is to create individual canvases for\r\neach sprite image and use those to draw rather than cutting frames from the original\r\nsprite sheet. No bleeding can occur because there are no longer pixels around the\r\nedges of the sprite image. */\r\n\r\n(function() { \"use strict\";\r\n\r\n  const Animation = function(frame_set, delay, mode = \"loop\") {\r\n\r\n    this.count       = 0;\r\n    this.delay       = delay;\r\n    this.frame_index = 0;\r\n    this.frame_set   = frame_set;\r\n    this.frame_value = frame_set[0];\r\n    this.mode = mode;\r\n\r\n  };\r\n\r\n  /* I expanded the Animation class to include play, loop, and rewind modes. They're\r\n  all really simple, and basically they are the same thing with very minor changes\r\n  dictating how the playhead or frame_index moves. */\r\n  Animation.prototype = {\r\n\r\n    constructor:Animation,\r\n\r\n    change:function(frame_set, delay = this.delay) {\r\n\r\n      if (frame_set != this.frame_set) {\r\n\r\n        this.count       = 0;\r\n        this.delay       = delay;\r\n        this.frame_index = 0;\r\n        this.frame_set   = frame_set;\r\n        this.frame_value = frame_set[0];\r\n\r\n      }\r\n\r\n    },\r\n\r\n    loop:function() {\r\n\r\n      this.count ++;\r\n\r\n      if (this.count >= this.delay) {\r\n\r\n        this.count = 0;\r\n\r\n        this.frame_index = (this.frame_index < this.frame_set.length - 1) ? this.frame_index + 1 : 0;\r\n        this.frame_value = this.frame_set[this.frame_index];\r\n\r\n      }\r\n\r\n    },\r\n\r\n    play:function() {\r\n\r\n      this.count ++;\r\n\r\n      if (this.count >= this.delay) {\r\n\r\n        this.count = 0;\r\n\r\n        if (this.frame_index < this.frame_set.length - 1) {\r\n\r\n          this.frame_index ++;\r\n          this.frame_value = this.frame_set[this.frame_index];\r\n\r\n        }\r\n\r\n      }\r\n\r\n    },\r\n\r\n    rewind:function() {\r\n\r\n      this.count ++;\r\n\r\n      if (this.count >= this.delay) {\r\n\r\n        this.count = 0;\r\n\r\n        if (this.frame_index > 0) {\r\n\r\n          this.frame_index --;\r\n          this.frame_value = this.frame_set[this.frame_index];\r\n\r\n        }\r\n\r\n      }\r\n\r\n    },\r\n\r\n    update:function() {\r\n\r\n      this[this.mode]();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* I added offsets to the frames. This allows me to group my frames close together\r\n  in the source image and save a lot of space in my image files. The offset is\r\n  applied when drawing the image to the screen, ensuring that the sprite always looks\r\n  centered and doesn't jump back and forth. */\r\n  const Frame = function(x, y, width, height, offset_x = 0, offset_y = 0) {\r\n\r\n    this.height   = height;\r\n    this.offset_x = offset_x;\r\n    this.offset_y = offset_y;\r\n    this.width    = width;\r\n    this.x        = x;\r\n    this.y        = y;\r\n\r\n  };\r\n\r\n  Frame.prototype = { constructor:Frame };\r\n\r\n  /* This simplifies creation of input keys. */\r\n  const Input = function(active, state) {\r\n\r\n    this.active = active;\r\n    this.state  = state;\r\n\r\n  };\r\n\r\n  Input.prototype = {\r\n\r\n    constructor:Input,\r\n\r\n    update:function(state) {\r\n\r\n      if (this.state != state) this.active = state;\r\n      this.state  = state;\r\n\r\n    }\r\n\r\n  };\r\n\r\n      //////////////////////\r\n    //// GAME CLASSES ////\r\n  //////////////////////\r\n\r\n  const Door = function(x, y, area, new_x) {\r\n\r\n    this.animation = new Animation(display.sprite_sheet.frame_set.door, 5, \"play\");\r\n    this.area = area;\r\n    this.new_x = new_x;\r\n    this.x = x;\r\n    this.y = y;\r\n\r\n  };\r\n\r\n  Door.prototype = {\r\n\r\n    constructor:Door,\r\n\r\n  };\r\n\r\n      ///////////////\r\n    //// LOGIC ////\r\n  ///////////////\r\n\r\n  var controller, display, game;\r\n\r\n  controller = {\r\n\r\n    down: new Input(false, false), left: new Input(false, false), right:new Input(false, false), up:new Input(false, false),\r\n\r\n    keyDownUp:function(event) { event.preventDefault();\r\n\r\n      var key_state = (event.type == \"keydown\") ? true : false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left.update(key_state); break;// left key\r\n        case 38: controller.up.update(key_state); break;// up key\r\n        case 39: controller.right.update(key_state); break;// right key\r\n        case 40: controller.down.update(key_state); break;// down key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    height_width_ratio:undefined,\r\n\r\n    sprite_sheet: {\r\n\r\n      frames:[new Frame(  0,  0, 27, 30), new Frame( 27,  0, 25, 30,  1),\r\n              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),\r\n              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),\r\n              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)],\r\n\r\n      frame_set: {\r\n\r\n        dominique_idle:[0, 1],\r\n        dominique_right:[2, 3, 4, 5],\r\n        dominique_left:[6, 7, 8, 9],\r\n        door:[10, 11, 12, 13, 14, 15, 16]\r\n\r\n      },\r\n\r\n      image:new Image()\r\n\r\n    },\r\n\r\n    render:function() {\r\n\r\n      var frame;\r\n\r\n      /* Draw the background. */\r\n      this.buffer.fillStyle = game.area.background_color;\r\n      this.buffer.fillRect(0, 0, game.area.width, game.area.height);\r\n\r\n      /* Draw the floor. */\r\n      this.buffer.fillStyle = \"#373641\";\r\n      this.buffer.fillRect(0, game.area.floor - 3, game.area.width, game.area.height - game.area.floor + 3);\r\n\r\n      /* Draw the doors. */\r\n      for (let index = game.area.doors.length - 1; index > -1; -- index) {\r\n\r\n        let door = game.area.doors[index];\r\n        frame = this.sprite_sheet.frames[door.animation.frame_value];\r\n\r\n        this.buffer.drawImage(this.sprite_sheet.image, frame.x, frame.y, frame.width, frame.height, door.x, door.y, frame.width, frame.height);\r\n\r\n      }\r\n\r\n      /* Draw Dominique. */\r\n      frame = this.sprite_sheet.frames[game.dominique.animation.frame_value];\r\n\r\n      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);\r\n\r\n      this.context.drawImage(this.buffer.canvas, 0, 0, game.area.width, game.area.height, 0, 0, this.context.canvas.width, this.context.canvas.height);\r\n\r\n      this.context.fillStyle = \"#ffffff\";\r\n      this.context.font = \"20px Arial\";\r\n      this.context.fillText(game.area.message, 10, 20);\r\n\r\n    },\r\n\r\n    resize:function(event) {\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n      if (display.context.canvas.width > document.documentElement.clientHeight - 16) {\r\n\r\n        display.context.canvas.width = document.documentElement.clientHeight - 16;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = display.context.canvas.width * display.height_width_ratio;\r\n\r\n      display.buffer.imageSmoothingEnabled = display.context.imageSmoothingEnabled = false;\r\n\r\n      display.render();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  game = {\r\n\r\n    area:undefined,\r\n\r\n    dominique: {\r\n\r\n      animation:new Animation(display.sprite_sheet.frame_set.dominique_idle, 15),\r\n      half_height:15,\r\n      half_width:10,\r\n      jumping:false,\r\n      velocity_x:0,\r\n      velocity_y:0,\r\n      x:100,\r\n      y:100,\r\n\r\n      collideWorld:function() {\r\n\r\n        if (this.x - this.half_width < 0) {\r\n\r\n          this.x = this.half_width;\r\n\r\n        } else if (this.x + this.half_width > game.area.width) {\r\n\r\n          if (game.area.message != \"~The Passageway~\") {\r\n\r\n            this.x = game.area.width - this.half_width;\r\n\r\n          } else if (this.x - this.half_width > game.area.width) {\r\n\r\n            game.engine.stop();\r\n            controller.right.active = false;\r\n\r\n            game.loadArea(\"area0.json\", function() {\r\n\r\n              game.reset();\r\n\r\n            });\r\n\r\n            alert(\"Dominique escaped the weird program full of pointless rooms and doors. She went to a much better, more interesting program.\");\r\n\r\n          }\r\n\r\n        }\r\n\r\n        if (this.y + this.half_height > game.area.floor) {\r\n\r\n          this.jumping = false;\r\n          this.velocity_y = 0;\r\n          this.y = game.area.floor - this.half_height;\r\n\r\n        }\r\n\r\n      },\r\n\r\n      update:function() {\r\n\r\n        this.velocity_y += 0.5;\r\n\r\n        this.x += this.velocity_x;\r\n        this.y += this.velocity_y;\r\n\r\n        this.velocity_x *= 0.9;\r\n        this.velocity_y *= 0.9;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    engine: {\r\n\r\n      accumulated_time:window.performance.now(),\r\n      frame_request:undefined,\r\n      time_step:1000/60,\r\n\r\n      loop:function(time_stamp) {\r\n\r\n        game.engine.frame_request = window.requestAnimationFrame(game.engine.loop);\r\n\r\n        if (controller.left.active) {\r\n\r\n          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_left, 15);\r\n          game.dominique.velocity_x -= 0.1;\r\n\r\n        }\r\n\r\n        if (controller.right.active) {\r\n\r\n          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_right, 15);\r\n          game.dominique.velocity_x += 0.1;\r\n\r\n        }\r\n\r\n        if (!controller.left.active && !controller.right.active) {\r\n\r\n          game.dominique.animation.change(display.sprite_sheet.frame_set.dominique_idle, 15);\r\n\r\n        }\r\n\r\n        if (controller.up.active && !game.dominique.jumping) {\r\n\r\n          controller.up.active = false;\r\n          game.dominique.jumping = true;\r\n          game.dominique.velocity_y = -5;\r\n\r\n        }\r\n\r\n        game.dominique.update();\r\n        game.dominique.collideWorld();\r\n\r\n        game.dominique.animation.update();\r\n\r\n        for (let index = game.area.doors.length - 1; index > -1; -- index) {\r\n\r\n          let door = game.area.doors[index];\r\n\r\n          if (game.dominique.x > door.x && game.dominique.x < door.x + 32) {\r\n\r\n            door.animation.mode = \"play\";\r\n\r\n            if (controller.down.active) { controller.down.active = false;\r\n\r\n              game.dominique.x = door.new_x + 1;\r\n              game.loadArea(door.area, game.reset);\r\n\r\n              return;\r\n\r\n            }\r\n\r\n          } else { door.animation.mode = \"rewind\"; }\r\n\r\n          game.area.doors[index].animation.update();\r\n\r\n        }\r\n\r\n        display.render();\r\n\r\n      },\r\n\r\n      start:function() {\r\n\r\n        this.accumulated_time = window.performance.now();\r\n        this.frame_request = window.requestAnimationFrame(this.loop);\r\n\r\n      },\r\n\r\n      stop:function() {\r\n\r\n        window.cancelAnimationFrame(this.frame_request);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    loadArea:function(url, callback) {\r\n\r\n      var request, readyStateChange;\r\n\r\n      request = new XMLHttpRequest();\r\n\r\n      readyStateChange = function(event) {\r\n\r\n        if (this.readyState == 4 && this.status == 200) {\r\n\r\n          game.area = JSON.parse(this.responseText);\r\n\r\n          callback();\r\n\r\n          game.engine.start();\r\n\r\n        }\r\n\r\n      };\r\n\r\n      request.addEventListener(\"readystatechange\", readyStateChange);\r\n      request.open(\"GET\", url);\r\n      request.send(null);\r\n\r\n      game.engine.stop();\r\n\r\n    },\r\n\r\n    reset:function() {\r\n\r\n      for (let index = game.area.doors.length - 1; index > -1; -- index) {\r\n\r\n        let door = game.area.doors[index];\r\n\r\n        game.area.doors[index] = new Door(door.x, game.area.floor - 32 - 3, door.area, door.new_x);\r\n\r\n      }\r\n\r\n      game.dominique.y = game.area.floor - game.dominique.half_height;\r\n      game.dominique.velocity_x = 0;\r\n\r\n      display.buffer.canvas.height = game.area.height;\r\n      display.buffer.canvas.width = game.area.width;\r\n      display.height_width_ratio = game.area.height / game.area.width;\r\n      display.resize();\r\n\r\n    }\r\n\r\n  };\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.sprite_sheet.image.addEventListener(\"load\", function(event) {\r\n\r\n    game.loadArea(\"area0.json\", function() {\r\n\r\n      game.reset();\r\n\r\n    });\r\n\r\n  });\r\n\r\n  display.sprite_sheet.image.src = \"dominiques-doors.png\";\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n\r\n  window.addEventListener(\"keydown\", controller.keyDownUp);\r\n  window.addEventListener(\"keyup\", controller.keyDownUp);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/elements/elements.html",
    "content": "<!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; display:grid; justify-items:center;\">\r\n\r\n    <h1 style = \"color:#ffffff; text-align:center\">PoP Vlog</h1>\r\n\r\n    <p id = \"output\" style = \"color:#ffffff;\">default</p>\r\n\r\n    <input id = \"input\" type = \"submit\" value = \"click me!\">\r\n\r\n    <script src = \"elements.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/elements/elements.js",
    "content": "// Frank Poth 08/03/2017\r\n\r\n\r\nvar input = document.getElementById(\"input\");\r\nvar output = document.getElementById(\"output\");\r\nvar counter = 0;\r\n\r\nvar click = function(event) {\r\n\r\n  counter += 1;\r\n  output.innerHTML = \"You clicked me \" + counter + \" times!\";\r\n\r\n};\r\n\r\ninput.addEventListener(\"click\", click);\r\n"
  },
  {
    "path": "content/gjk/gjk.html",
    "content": "<!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 = \"description\" content = \"An implementation of the GJK collision detection algorithm written in JavaScript.\">\r\n    <title>GJK Collision</title>\r\n    <style>\r\n      * { margin:0; overflow:hidden; padding:0; pointer-events:none; user-select:none; }\r\n      html, body{ height:100%; width:100%; }\r\n      body { background-color:#000000; display:grid; }\r\n      #canvas { align-self:center; justify-self:center; }\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas id = \"canvas\"></canvas>\r\n\r\n    <script type = \"text/javascript\">var o = document.querySelector(\"p\");\r\n\r\n      class ConvexShape2D {\r\n\r\n        constructor() {}\r\n\r\n        collide(shape, d) {\r\n\r\n          var a = this.getExtremePoint(d[0], d[1]);// get the extreme point on this shape\r\n          var b = shape.getExtremePoint(-d[0], -d[1]);// get the opposite extreme point on the other shape\r\n          var p = [a[0] - b[0], a[1] - b[1]];// get the extreme point on the Minkowski Difference shape\r\n\r\n          if (p[0] * d[0] + p[1] * d[1] < 0) return false;// if the origin is beyond p in direction d, return false\r\n  \r\n          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\r\n          var s = [[p[0], p[1]]];// the simplex now has one point in it\r\n\r\n          d = [-p[0], -p[1]];// search in the direction of the origin next\r\n\r\n          while(true) {\r\n\r\n            a = this.getExtremePoint(d[0], d[1]);// get the extreme point on this shape\r\n            b = shape.getExtremePoint(-d[0], -d[1]);// get the opposite extreme point on the other shape\r\n            p = [a[0] - b[0], a[1] - b[1]];// get the extreme point on the Minkowski Difference shape\r\n\r\n            if (p[0] * d[0] + p[1] * d[1] < 0) return false;// if the origin is beyond p in direction d, return false\r\n\r\n            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.\r\n\r\n            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\r\n            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)\r\n\r\n              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.\r\n              \r\n              if (c == tc) return true;// collision!\r\n              else {// no collision and the winding has changed\r\n\r\n                s.shift();\r\n                tc = c;\r\n\r\n              }\r\n\r\n            } else {\r\n\r\n              c = tc;\r\n              s.pop();\r\n\r\n            }\r\n\r\n            if (tc) {\r\n\r\n              d[0] = p[1] - s[0][1];\r\n              d[1] = s[0][0] - p[0];\r\n\r\n            } else {\r\n\r\n              d[0] = s[0][1] - p[1];\r\n              d[1] = p[0] - s[0][0];\r\n\r\n            }\r\n\r\n            s.unshift(p);\r\n\r\n          }\r\n\r\n        }\r\n\r\n        getExtremePoint(vx, vy) {}\r\n\r\n        moveTo() {}\r\n\r\n      }\r\n\r\n      class Circle extends ConvexShape2D {\r\n\r\n        constructor(x, y, r) {\r\n\r\n          super();\r\n\r\n          this.x = x; this.y = y; this.r = r;\r\n\r\n        }\r\n\r\n        getExtremePoint(vx, vy) {\r\n          \r\n          var d = Math.atan2(vy, vx);\r\n\r\n          vx = Math.cos(d) * this.r;\r\n          vy = Math.sin(d) * this.r;\r\n\r\n          return [this.x + vx, this.y + vy];\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Polygon extends ConvexShape2D {\r\n\r\n        constructor(x, y, vertices) {\r\n\r\n          super();\r\n\r\n          this.x = this.y = 0;\r\n\r\n          this.vertices = [...vertices];\r\n\r\n          this.moveTo(x, y);\r\n\r\n        }\r\n\r\n        getExtremePoint(vx, vy) {\r\n\r\n          var p = [this.vertices[0], this.vertices[1]];\r\n          var ml = this.vertices[0] * vx + this.vertices[1] * vy\r\n\r\n          for (var index = this.vertices.length - 2; index > 1; index -= 2) {\r\n\r\n            let tl = this.vertices[index] * vx + this.vertices[index + 1] * vy;\r\n\r\n            if (tl > ml) { \r\n\r\n              ml = tl\r\n              p = [this.vertices[index], this.vertices[index + 1]];\r\n\r\n            }\r\n\r\n          }\r\n\r\n          return p;\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          var vx = x - this.x;\r\n          var vy = y - this.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; index -= 2) {\r\n\r\n            this.vertices[index - 1] += vx;\r\n            this.vertices[index]     += vy;\r\n\r\n          }\r\n\r\n          this.x += vx;\r\n          this.y += vy;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var context = document.getElementById(\"canvas\").getContext(\"2d\", { alpha:false });\r\n\r\n      var shapes = [\r\n        new Circle( 100, 100, 20),\r\n        new Polygon(100, 200, [0, -20, 20, 0, 20, 20, -20, 20, -20, 0]),\r\n        new Polygon(100, 300, [-20, -20, 20, -20, 20, 20, -20, 20]),\r\n        new Polygon(100, 400, [0, -20, 20, 20, -20, 20])\r\n      ];\r\n\r\n      var pointer = { x:0, y:0, down:false, getExtremePoint:function() { return [this.x, this.y]; } }\r\n\r\n      var selected_shape = undefined;\r\n\r\n      function drawShape(shape, c) {\r\n\r\n        switch(shape.constructor) {\r\n\r\n          case Circle: drawCircle(shape, c); break;\r\n          case Polygon: drawPolygon(shape.vertices, c);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function drawCircle(circle, c = \"#0080f0\") {\r\n\r\n        context.beginPath();\r\n        context.fillStyle = c;\r\n        context.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2);\r\n        context.closePath();\r\n        context.fill();\r\n\r\n      }\r\n\r\n      function drawPolygon(vertices, c = \"#0080f0\") { \r\n\r\n        context.beginPath();\r\n        context.fillStyle = c;\r\n        context.moveTo(vertices[0], vertices[1]);\r\n        \r\n        for (let index = vertices.length - 1; index > 0; index -= 2) {\r\n\r\n          context.lineTo(vertices[index - 1], vertices[index]);\r\n\r\n        }\r\n\r\n        context.closePath();\r\n        context.fill()\r\n\r\n      }\r\n\r\n      function loop(time_stamp) {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        if (pointer.down && selected_shape == undefined) {\r\n\r\n          for (let index = shapes.length - 1; index > -1; -- index) {\r\n\r\n            let shape = shapes[index];\r\n\r\n            if (shape.collide(pointer, [1, 0])) {\r\n              \r\n              selected_shape = shapes.splice(index, 1)[0];\r\n              break;\r\n\r\n            }\r\n\r\n          }\r\n\r\n        }\r\n\r\n        let colliding = false;\r\n\r\n        for (let index = shapes.length - 1; index > -1; -- index) {\r\n\r\n          let shape = shapes[index];\r\n\r\n          if (selected_shape && selected_shape.collide(shape, [1, 0])) {\r\n            \r\n            drawShape(shape, \"#f08000\");\r\n            colliding = true;\r\n\r\n          } else {\r\n            \r\n            drawShape(shape, \"#0080f0\");\r\n\r\n          }\r\n\r\n        }\r\n\r\n        if (selected_shape) {\r\n\r\n          selected_shape.moveTo(pointer.x, pointer.y);  \r\n          if (colliding) drawShape(selected_shape, \"#f08000\");\r\n          else drawShape(selected_shape, \"#0080f0\");\r\n        \r\n        }\r\n\r\n        if (!pointer.down && selected_shape) {\r\n          \r\n          shapes.push(selected_shape);\r\n          selected_shape = undefined;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function mouseDownMoveUp(event) {\r\n\r\n        var r = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - r.left;\r\n        pointer.y = event.clientY - r.top;\r\n\r\n        switch(event.type) {\r\n\r\n          case \"mousedown\": pointer.down = true; break;\r\n          case \"mouseup\": pointer.down = false;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n        context.canvas.width  = document.documentElement.clientWidth  - 16;\r\n\r\n      }\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      window.addEventListener(\"mousedown\", mouseDownMoveUp);\r\n      window.addEventListener(\"mousemove\", mouseDownMoveUp);\r\n      window.addEventListener(\"mouseup\", mouseDownMoveUp);\r\n\r\n      resize();\r\n      loop();\r\n    \r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/hello-world/hello.html",
    "content": "<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, world!</h1>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      alert(\"Hello, world!\");\r\n      console.log(\"Hello, world (from the console)!\");\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#303840;\r\n  justify-self:center;\r\n\r\n}\r\n"
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.html",
    "content": "<!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    <meta name = \"description\" content = \"PoP Vlog 20 - Learn how to do basic collision detection and response in a 2d tile based game world.\">\r\n    <meta name = \"veiwport\" content = \"width=device-width\">\r\n\r\n    <title>PoP Vlog - Hit The Wall</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Hit The Wall</h1>\r\n\r\n    <p>&nbsp;</p> <!-- the output p element -->\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>This example requires a keyboard.</p>\r\n\r\n    <script src = \"hit-the-wall.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/hit-the-wall/hit-the-wall.js",
    "content": "// 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 controller, display, game;\r\n\r\n  // a basic controller object that handles key input\r\n  controller = {\r\n\r\n    left:false,\r\n    right:false,\r\n    up:false,\r\n\r\n    keyUpDown:function(event) {\r\n\r\n      var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left = key_state; break; // left key\r\n        case 38: controller.up = key_state; break; // up key\r\n        case 39: controller.right = key_state; break; // right key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // draws everything and handles html elements\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    output:document.querySelector(\"p\"),\r\n\r\n    render:function() {\r\n\r\n      for (let index = game.world.map.length - 1; index > -1; -- index) {\r\n\r\n        this.buffer.fillStyle = (game.world.map[index] > 0)?(\"#0099\" + game.world.map[index] + \"f\"):\"#303840\";\r\n        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);\r\n\r\n      }\r\n\r\n      this.buffer.fillStyle = game.player.color;\r\n      this.buffer.fillRect(game.player.x, game.player.y, game.player.width, game.player.height);\r\n\r\n      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);\r\n\r\n    },\r\n\r\n    resize:function(event) {\r\n\r\n      var client_height = document.documentElement.clientHeight;\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n      if (display.context.canvas.width > client_height) {\r\n\r\n        display.context.canvas.width = client_height;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = Math.floor(display.context.canvas.width * 0.625);\r\n\r\n      display.render();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // here's where things get interesting\r\n  // the game object holds all of our awesome game logic\r\n  game = {\r\n\r\n    // there's something different about the player object, and its old_x and\r\n    // old_y. these variables keep track of the last position the player occupied\r\n    player: {\r\n\r\n      color:\"#ff9900\",\r\n      height:32,\r\n      jumping:false,\r\n      old_x:160,// used for tracking last position for collision detection\r\n      old_y:160,\r\n      velocity_x:0,\r\n      velocity_y:0,\r\n      width:24,\r\n      x:160,\r\n      y:90\r\n\r\n    },\r\n\r\n    // the world object holds information about our tile map\r\n    world: {\r\n\r\n      columns:8,// just some basic info. no worries\r\n      rows:5,\r\n      tile_size:40,\r\n\r\n      map:[0,0,0,0,0,0,0,0,// I went with a smaller map this time\r\n           0,0,0,0,0,0,0,0,// 0s represent walkable tiles and everything else\r\n           1,0,0,0,0,0,0,2,// represents a collision tile or wall tile\r\n           3,0,0,4,0,0,2,5,// the different numbers correspond to different\r\n           5,5,5,5,5,5,5,5]// collision shapes.\r\n\r\n    },\r\n\r\n    // the collision object is used to handle narrow phase collision detection\r\n    // and response. Broad phase collision detection is just determining if the\r\n    // player is standing on a non 0 tile in the map, narrow phase is handled\r\n    // here, where the player's exact position is tested against the specific\r\n    // collision boundaries of each type of tile. In this example there are 5\r\n    // types of collision tile\r\n    collision: {\r\n\r\n      // the reason these functions are indexed with numbers is because they\r\n      // correspond directly to tile values in the map array. For instance, this\r\n      // function handles collision for any tile in the map with a value of 1\r\n      // it handles collision detection and response on the specified player object\r\n      // at the specified row and column in the tile map. the 1 tile collision\r\n      // shape has a flat top and a flat right side, so only test for collision\r\n      // and do response on those sides.\r\n      1:function(object, row, column) {\r\n\r\n        /* Basically these indexed functions are like routing functions. They\r\n        make it easy to connect a collision shape with a value in the tile map.\r\n        The real collision code happens in these inner functions. The great thing\r\n        about this approach is that it's extremely modular, so you can mix and\r\n        match collision functions and easily introduce new ones. */\r\n\r\n        /* Depending on whether you want to do y or x first collision is as simple\r\n        as calling the collision function for a specific axis before the other.\r\n        In a platformer where players are always falling down due to gravity, it's\r\n        a good idea to do y collision detection first. */\r\n\r\n        if (this.topCollision(object, row)) { return; }// if no top collision\r\n        this.rightCollision(object, column);           // try right side collision\r\n\r\n      },\r\n\r\n      // the 2 tile type has a top and a left side to collide with\r\n      2:function(object, row, column) {\r\n\r\n        if (this.topCollision(object, row)) { return; }\r\n        this.leftCollision(object, column);\r\n\r\n      },\r\n\r\n      // the 3 tile type only blocks movement through the right side\r\n      3:function(object, row, column) {\r\n\r\n        this.rightCollision(object, column);\r\n\r\n      },\r\n\r\n      // the 4 tile type has collision on all sides but the bottom\r\n      4:function(object, row, column) {\r\n\r\n        if (this.topCollision(object, row)) { return; }// you only want to do one\r\n        if (this.leftCollision(object, column)) { return; }// of these collision\r\n        this.rightCollision(object, column);// responses. that's why there are if statements\r\n\r\n      },\r\n\r\n      // the 5 tile only does collision if the object falls through the top\r\n      5:function(object, row, column) {\r\n\r\n        this.topCollision(object, row);\r\n\r\n      },\r\n\r\n      /* Here are the actual collision detection and response functions. The concept\r\n      I am working off of with this example is that all tiles have 4 sides. Rather\r\n      than doing collision for all of them in the same function, I want to mix and\r\n      match collision methods for each side. By doing it this way I can create all\r\n      sorts of collision shapes and resuse my side specific collision code. This\r\n      works extremely well, and the only downside is having to be more specific\r\n      when you lay out your level maps because each tile has a specific purpose.\r\n      For example, a floor tile only tests for collision on its top side and a wall\r\n      tile will only test for collision on the left and/or right side. It is a small\r\n      price to pay for awesome and smooth collision detection with basically no restriction on\r\n      tile based collision shapes. You can have tiles where one side is a slope and the\r\n      other is a curve or a flat side, for instance. That's pretty sweet. */\r\n\r\n      /* This function handles collision with a rightward moving object. Another\r\n      design requirement to use this method is that objects must have a record of\r\n      their current and last physical position. A collision can only occur when a\r\n      player enters into a collision shape through its boundary. It's foolproof so\r\n      long as you always spawn your players on empty tiles and not in the walls. */\r\n      leftCollision(object, column) {\r\n\r\n        if (object.velocity_x > 0) {// If the object is moving right\r\n\r\n          var left = column * game.world.tile_size;// calculate the left side of the collision tile\r\n\r\n          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\r\n\r\n            object.velocity_x = 0;// Stop moving\r\n            object.x = object.old_x = left - object.width * 0.5 - 0.001;// place object outside of collision\r\n            // the 0.001 is just to ensure that the object is no longer in the same tile space as the collision tile\r\n            // due to the way object tile position is calculated, moving the object to the exact boundary of the collision tile\r\n            // 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\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      },\r\n\r\n      // these are all basically the same concept as the leftCollision function,\r\n      // only for the different sides.\r\n\r\n      rightCollision(object, column) {\r\n\r\n        if (object.velocity_x < 0) {\r\n\r\n          var right = (column + 1) * game.world.tile_size;\r\n\r\n          if (object.x + object.width * 0.5 < right && object.old_x + object.width * 0.5 >= right) {\r\n\r\n            object.velocity_x = 0;\r\n            object.old_x = object.x = right - object.width * 0.5;\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      },\r\n\r\n      topCollision(object, row) {\r\n\r\n        if (object.velocity_y > 0) {\r\n\r\n          var top = row * game.world.tile_size;\r\n\r\n          if (object.y + object.height > top && object.old_y + object.height <= top) {\r\n\r\n            object.jumping = false;\r\n            object.velocity_y = 0;\r\n            object.old_y = object.y = top - object.height - 0.01;\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    // Here's the game loop, where it all goes down!!!\r\n    loop:function() {\r\n\r\n      // get and use keyboard input\r\n      if (controller.left) {\r\n\r\n        game.player.velocity_x -= 0.25;\r\n\r\n      }\r\n\r\n      if (controller.right) {\r\n\r\n        game.player.velocity_x += 0.25;\r\n\r\n      }\r\n\r\n      if (controller.up && !game.player.jumping) {\r\n\r\n        game.player.velocity_y = -16;\r\n        game.player.jumping = true;\r\n\r\n      }\r\n\r\n      game.player.velocity_y += 1; // add gravity\r\n\r\n      game.player.old_x = game.player.x;// store the last position of the player\r\n      game.player.old_y = game.player.y;// before we move it on this cycle\r\n\r\n      game.player.x += game.player.velocity_x;// move the player's current position\r\n      game.player.y += game.player.velocity_y;\r\n\r\n      // do collision detection with the level boundaries so the player can't leave\r\n      // the screen. Nothing you haven't seen before...\r\n      if (game.player.x < 0) {\r\n\r\n        game.player.velocity_x = 0;\r\n        game.player.old_x = game.player.x = 0;\r\n\r\n      } else if (game.player.x + game.player.width > display.buffer.canvas.width) {\r\n\r\n        game.player.velocity_x = 0;\r\n        game.player.old_x = game.player.x = display.buffer.canvas.width - game.player.width;\r\n\r\n      }\r\n\r\n      if (game.player.y < 0) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.old_y = game.player.y = 0;\r\n\r\n      } else if (game.player.y + game.player.height > display.buffer.canvas.height) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.old_y = game.player.y = display.buffer.canvas.height - game.player.height;\r\n\r\n      }\r\n\r\n      // NOW FOR SOME GOOD STUFF!!! Here we do broadphase collision detection by checking\r\n      // which tile value the player is standing on. If it is anything but 0 we have a possible collision.\r\n\r\n      // calculate the player's x and y tile position in the tile map\r\n      var tile_x = Math.floor((game.player.x + game.player.width * 0.5) / game.world.tile_size);\r\n      var tile_y = Math.floor((game.player.y + game.player.height) / game.world.tile_size);\r\n      // get the value at the tile position in the map\r\n      var value_at_index = game.world.map[tile_y * game.world.columns + tile_x];\r\n\r\n      // do some output so we can see it all in action\r\n      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];\r\n\r\n      // if it's not an empty tile, we need to do narrow phase collision detection and possibly response!\r\n      if (value_at_index != 0) {\r\n\r\n        // simply call one of the routing functions in the collision object and pass\r\n        // in values for the collision tile's location in grid/map space\r\n        game.collision[value_at_index](game.player, tile_y, tile_x);\r\n\r\n      }\r\n\r\n      // This might be confusing at first, but we actually have to do this twice, since\r\n      // we are using a player object with a single point of contact, which is its bottom middle.\r\n      // say we handled a collision with the floor in the check above and now the player object is\r\n      // moved up above that tile. There's still a posibility that the player was moved into\r\n      // an adjacent wall tile! We need to recalculate the new tile space the player is in\r\n      // and then check to see if there is another collision. Don't worry, we only need\r\n      // to do this twice. Once for the x axis, and one for the y axis. With this\r\n      // collision method they could happen in either order, but they will both be handled.\r\n\r\n      tile_x = Math.floor((game.player.x + game.player.width * 0.5) / game.world.tile_size);\r\n      tile_y = Math.floor((game.player.y + game.player.height) / game.world.tile_size);\r\n      value_at_index = game.world.map[tile_y * game.world.columns + tile_x];\r\n\r\n      if (value_at_index != 0) {\r\n\r\n        game.collision[value_at_index](game.player, tile_y, tile_x);\r\n\r\n      } // and that's it! You checked twice and resolved any collisions with the map!\r\n\r\n      game.player.velocity_x *= 0.9;// apply some friction to the player's velocity\r\n      game.player.velocity_y *= 0.9;// the reason we do this after the collision code\r\n      // is because we use the players velocity in the collision code and don't want to change it before now.\r\n      // it probably doesn't matter, though. You could also calculate the velocity for collision by\r\n      // subtracting the players last position from its current position, that never fails.\r\n\r\n      display.render();\r\n\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // basic setup and initialization\r\n  display.buffer.canvas.height = 200;\r\n  display.buffer.canvas.width = 320;\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n\r\n  display.resize();\r\n\r\n  game.loop();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/hitbox/hitbox.html",
    "content": "<!DOCTYPE html>\r\n\r\n<html>\r\n\r\n  <head>\r\n        \r\n    <meta name = \"description\" content = \"Interactive example of player vs player hitbox collision with full JavaScript source code.\">\r\n    <meta name = \"viewport\" content = \"width=device-width user-scalable=no\">\r\n    \r\n    <style>\r\n      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; user-select:none; }\r\n      body, html { height:100%; width:100%; }\r\n      body { background-color:#202830; display:grid; }\r\n      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; }\r\n      canvas { align-self:center; justify-self:center; }\r\n    </style>\r\n    \r\n    <title>Hitbox</title>\r\n    \r\n  </head>\r\n  \r\n  <body>\r\n\r\n    <a>freeze</a>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      /* 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. */\r\n\r\n      /* 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. */\r\n      class Rectangle {\r\n\r\n        constructor(l, t, w, h) { // left, top, width, height\r\n\r\n          this.l = this.ol = l; // left and old left\r\n          this.r = this.or = l + w; // right and old right\r\n          this.t = this.ot = t; // top and old top\r\n          this.b = this.ob = t + h; // bottom and old bottom\r\n          this.w = w; // width\r\n          this.h = h; // height\r\n          this.vx = this.vy = 0; // velocity x and y\r\n\r\n        }\r\n\r\n        /* 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. */\r\n        setBottom(b) { this.b = b; this.t = b - this.h; }\r\n        setLeft(l)   { this.l = l; this.r = l + this.w; }\r\n        setRight(r)  { this.r = r; this.l = r - this.w; }\r\n        setTop(t)    { this.t = t; this.b = t + this.h; }\r\n\r\n      }\r\n\r\n      /* 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). */ \r\n      class Platform extends Rectangle {\r\n\r\n        constructor(l, t, w, h) {\r\n\r\n          super(l, t, w, h);\r\n\r\n          this.d = Math.random() * Math.PI * 2; // initial movement direction\r\n          this.rotation = Math.random() * Math.PI * 2; // rotation for sine wave movement\r\n\r\n          this.frozen = false;\r\n\r\n        }\r\n\r\n        collideBoundaries(l, r, t, b) { // collide with the walls\r\n\r\n          if (this.l < l) {\r\n\r\n            this.d = Math.atan2(Math.sin(this.d), Math.cos(this.d) * -1);\r\n            this.vx *= -1;\r\n            this.setLeft(l);\r\n\r\n          } else if (this.r > r) {\r\n\r\n            this.d = Math.atan2(Math.sin(this.d), Math.cos(this.d) * -1);\r\n            this.vx *= -1;\r\n            this.setRight(r);\r\n\r\n          }\r\n\r\n          if (this.t <= t) {\r\n\r\n            this.d = Math.atan2(Math.sin(this.d) * -1, Math.cos(this.d));\r\n            this.vy *= -1;\r\n            this.setTop(t);\r\n\r\n          } else if (this.b > b) {\r\n\r\n            this.d = Math.atan2(Math.sin(this.d) * -1, Math.cos(this.d));\r\n            this.vy *= -1;\r\n            this.setBottom(b);\r\n\r\n          }\r\n\r\n        }\r\n\r\n        update() {\r\n\r\n          if (!this.frozen) {\r\n            \r\n            this.vx = Math.cos(this.d); // move in direction\r\n            this.vy = Math.sin(this.d);\r\n\r\n            this.rotation += 0.05;\r\n            this.vy += Math.sin(this.rotation); // sine wave motion\r\n\r\n          }\r\n\r\n          this.ob = this.b; // update the old positions to the current positions\r\n          this.ol = this.l;\r\n          this.or = this.r;\r\n          this.ot = this.t;\r\n\r\n          this.l += this.vx; // update the current positions to the new positions\r\n          this.t += this.vy;\r\n          this.r = this.l + this.w;\r\n          this.b = this.t + this.h;\r\n          \r\n        }\r\n\r\n      }\r\n\r\n      class Player extends Rectangle {\r\n\r\n        constructor(l, t, w, h) {\r\n\r\n          super(l, t, w, h);\r\n\r\n          this.jumping = true;\r\n\r\n        }\r\n\r\n        collideRectangle(rectangle) {\r\n\r\n          if (this.b < rectangle.t || this.t > rectangle.b || this.l > rectangle.r || this.r < rectangle.l) return;\r\n\r\n          /* 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. */\r\n          if (this.b >= rectangle.t && this.ob < rectangle.ot) {\r\n\r\n            this.setBottom(rectangle.t - 0.1);\r\n            this.vy = rectangle.vy; // the platform moves the player with it after collision...\r\n            this.jumping = false;\r\n\r\n          } else if (this.t <= rectangle.b && this.ot > rectangle.ob) {\r\n\r\n            this.setTop(rectangle.b + 0.1);\r\n            this.vy = rectangle.vy; // ... regardless of what side the player collides with\r\n\r\n          } else if (this.r >= rectangle.l && this.or < rectangle.ol) {\r\n\r\n            this.setRight(rectangle.l - 0.1);\r\n            this.vx = rectangle.vx;\r\n\r\n          } else if (this.l <= rectangle.r && this.ol > rectangle.or) {\r\n\r\n            this.setLeft(rectangle.r + 0.1);\r\n            this.vx = rectangle.vx;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        update(g, f) { // gravity and friction\r\n\r\n          this.vy += g; // you can make updates to velocity before or after the position update\r\n\r\n          this.vx *= f; // I choose before so there isn't one frame of inactivity on the first cycle\r\n          this.vy *= f;\r\n\r\n          this.ob = this.b; // update the old positions to the current positions\r\n          this.ol = this.l;\r\n          this.or = this.r;\r\n          this.ot = this.t;\r\n\r\n          this.l += this.vx; // update the current positions to the new positions\r\n          this.t += this.vy;\r\n          this.r = this.l + this.w;\r\n          this.b = this.t + this.h;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var gravity = 1;\r\n      var friction = 0.9;\r\n\r\n      var player   = new Player(context.canvas.width * 0.25, 0, 32, 32, \"#0080f0\");\r\n      var platforms = [];\r\n\r\n      var pointer = { x:player.l, down:false };\r\n      \r\n      function getFloor() {\r\n\r\n        return context.canvas.height * 0.9;\r\n\r\n      }\r\n\r\n      function collideFloor(player) {\r\n\r\n        var floor = getFloor();\r\n\r\n        if (player.b > floor) {\r\n\r\n          player.setBottom(floor);\r\n          player.vy      = 0;\r\n          player.jumping = false;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function loop(time_stamp) {\r\n\r\n        update();\r\n        render();\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n      }\r\n\r\n      function render() {\r\n\r\n        context.fillStyle = \"#f0d8c0\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        context.fillStyle = \"#202830\";\r\n        context.fillRect(0, getFloor(), context.canvas.width, 8);\r\n\r\n        context.fillStyle = \"#0080f0\";\r\n        context.fillRect(player.l, player.t, player.w, player.h);\r\n\r\n        context.font = \"20px Arial\";\r\n\r\n        for (let index = platforms.length - 1; index > -1; -- index) {\r\n\r\n          let platform = platforms[index];\r\n\r\n          context.fillStyle = \"#f08000\";\r\n          context.fillRect(platform.l, platform.t, platform.w, platform.h);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function update() {\r\n\r\n        player.vx += (pointer.x - player.l - player.w * 0.5) * 0.01;\r\n\r\n        if (pointer.down && !player.jumping) {\r\n\r\n          player.jumping = true;\r\n          player.vy -= player.h + 1;\r\n          pointer.down = false;\r\n\r\n        }\r\n\r\n        player.update(gravity, friction);\r\n\r\n        collideFloor(player);\r\n\r\n        for (let index = platforms.length - 1; index > -1; -- index) {\r\n\r\n          let platform = platforms[index]; \r\n\r\n          platform.update();\r\n\r\n          platform.collideBoundaries(0, context.canvas.width, 0, getFloor());\r\n\r\n          player.collideRectangle(platform);\r\n\r\n        }\r\n\r\n      }\r\n\r\n        /////////////////////////\r\n       //// EVENT LISTENERS ////\r\n      /////////////////////////\r\n\r\n      function mouseDownUp(event) { event.preventDefault();\r\n      \r\n        pointer.down = event.type == \"mousedown\" ? true : false;\r\n      \r\n      }\r\n\r\n      function mouseMove(event) { event.preventDefault();\r\n\r\n        pointer.x = event.clientX - context.canvas.getBoundingClientRect().left;\r\n\r\n      }\r\n\r\n      function touchEndStart(event) { event.preventDefault();\r\n      \r\n        pointer.down = event.type == \"touchstart\" ? true : false;\r\n\r\n        if (pointer.down) pointer.x = event.targetTouches[0].clientX - context.canvas.getBoundingClientRect().left;\r\n      \r\n      }\r\n\r\n      function touchMove(event) { event.preventDefault();\r\n      \r\n        pointer.x = event.targetTouches[0].clientX - context.canvas.getBoundingClientRect().left;\r\n      \r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 32;\r\n        context.canvas.width  = document.documentElement.clientWidth  - 32;\r\n\r\n      }\r\n\r\n        ////////////////////\r\n       //// INITIALIZE ////\r\n      ////////////////////\r\n\r\n      resize();\r\n\r\n      // consolidate event listeners that use the same event handler (probably overkill)\r\n      [\"mousedown\", \"mouseup\"].map(type => window.addEventListener(type, mouseDownUp));\r\n      [\"touchend\", \"touchstart\"].map(type => window.addEventListener(type, touchEndStart));\r\n\r\n      window.addEventListener(\"mousemove\",  mouseMove);\r\n      window.addEventListener(\"touchmove\",  touchMove);\r\n      window.addEventListener(\"resize\",     resize);\r\n      window.addEventListener(\"contextmenu\", (event) => { event.preventDefault(); });\r\n\r\n      [\"click\", \"touchstart\"].map(type => document.querySelector(\"a\").addEventListener(type, (event) => { event.preventDefault();\r\n        \r\n        for (let index = platforms.length - 1; index > -1; -- index) {\r\n\r\n          let platform = platforms[index];\r\n\r\n          platform.frozen = !platform.frozen;\r\n\r\n          if (platform.frozen) platform.vx = platform.vy = 0;\r\n\r\n        }\r\n\r\n      }));\r\n\r\n      (() => { // change scope to keep these variables out of the global scope\r\n\r\n        var w = context.canvas.width;\r\n        var h = context.canvas.height;\r\n\r\n        var columns = Math.floor(w / 128);\r\n        var rows    = Math.floor(h / 128);\r\n\r\n        for (let column = columns; column > 0; column --) {\r\n\r\n          for (let row = rows; row > 0; row --) {\r\n\r\n            platforms.push(new Platform(column * 128 - 64, row * 128 - 64, Math.random() * (w / columns), Math.random() * (h / rows) * 0.5 + 1));\r\n\r\n          }\r\n\r\n        }\r\n\r\n      })();\r\n\r\n      loop();\r\n        \r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/https-server/index.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:center;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n"
  },
  {
    "path": "content/https-server/index.html",
    "content": "<!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 = \"width=device-width\">\r\n\r\n    <link href = \"/index.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - HTTPS Server!</h1>\r\n    <br>\r\n    <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>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/https-server/server.js",
    "content": "// 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\"); // file system\r\n  https = require(\"https\"); // creates an https server\r\n  path = require(\"path\"); // used for working with url paths\r\n\r\n  // used to create response headers\r\n  /* If the user requests a .css file, we want to ensure we attach \"text/css\" to\r\n  our response header, this way the browser knows how to handle it. */\r\n  mimetypes = {\r\n\r\n    \"css\":\"text/css\",\r\n    \"html\":\"text/html\",\r\n    \"ico\":\"image/ico\",\r\n    \"jpg\":\"image/jpeg\",\r\n    \"js\":\"text/javascript\",\r\n    \"json\":\"application/json\",\r\n    \"png\":\"image/png\"\r\n\r\n  };\r\n\r\n  options = {\r\n\r\n    pfx: fs.readFileSync(\"ssl/crt.pfx\"),\r\n    passphrase: \"password\"\r\n\r\n  };\r\n\r\n  // Start a secure server that uses the credentials in ssl/crt.pfx\r\n  server = https.createServer(options, function(request, response) {\r\n\r\n    /* When requesting the homepage of a website, we usually only type\r\n    www.mysite.com, but the server returns www.mysite.com/index.html. To make\r\n    it easier for users to access our site, we add \"/index.html\" to their url\r\n    so the user doesn't have to type out the whole address of our home page. */\r\n\r\n    // If the url is empty\r\n    if (request.url == \"\" || request.url == \"/\") {\r\n\r\n      // The user is requesting the home page of the website, so give it to them\r\n      request.url = \"index.html\";\r\n\r\n    }\r\n\r\n    // Next we read the file at the requested url and write it to the document.\r\n    /* __dirname is just the base directory of your website, so if your website\r\n    is www.coolsite.com, then __dirname is www.coolsite.com. When you put it all\r\n    together it looks like www.coolsite.com/index.html or whatever the requested\r\n    url is */\r\n    fs.readFile(__dirname + \"/\" + request.url, function(error, content) {\r\n\r\n      if (error) { // if there is an error reading the requested url\r\n\r\n        console.log(\"Error: \" + error); // output it to the console\r\n\r\n      } else { // else, there is no error, write the file contents to the page\r\n\r\n        // 200 is code for OK, and the second parameter is our content header\r\n        response.writeHead(200, {'Content-Type':mimetypes[path.extname(request.url).split(\".\")[1]]});\r\n        response.write(content); // write that content to our response object\r\n\r\n      }\r\n\r\n      response.end(); // This will send our response object to the browser\r\n\r\n    });\r\n\r\n  });\r\n\r\n  server.listen(\"2468\", \"192.168.0.101\", function() {\r\n\r\n    console.log(\"Server started!\");\r\n\r\n  }); // listen on 192.168.0.101:2468\r\n\r\n})();\r\n"
  },
  {
    "path": "content/https-server/ssl/crt.cnf",
    "content": "[req]\r\ndays                   = 180\r\nserial                 = 1\r\ndistinguished_name     = req_distinguished_name\r\nx509_extensions        = v3_ca\r\n\r\n[req_distinguished_name]\r\ncountryName            = Country\r\nstateOrProvinceName    = State\r\nlocalityName           = Locality\r\norganizationName       = Organizational Name\r\norganizationalUnitName = Organizational Unit Name\r\ncommonName             = Common Name (Domain Name)\r\nemailAddress           = Email Address\r\n\r\n\r\n[ v3_ca ]\r\n# The extentions to add to a self-signed cert\r\nsubjectKeyIdentifier   = hash\r\nauthorityKeyIdentifier = keyid:always,issuer:always\r\n\r\n# THIS IS VERY IMPORTANT IF YOU WANT TO USE THIS CERTIFICATION AS AN AUTHORITY!!!\r\nbasicConstraints       = CA:TRUE\r\n\r\nkeyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign\r\nsubjectAltName         = DNS:192.168.0.101, IP:192.168.0.101\r\nissuerAltName          = issuer:copy\r\n"
  },
  {
    "path": "content/https-server/ssl/csr.cnf",
    "content": "[req]\r\n\r\ndays                   = 180\r\ndistinguished_name     = req_distinguished_name\r\nreq_extensions         = v3_req\r\n\r\n[req_distinguished_name]\r\n\r\ncountryName            = Country\r\nstateOrProvinceName    = State\r\nlocalityName           = Locality\r\norganizationName       = Organizational Name\r\norganizationalUnitName = Organizational Unit Name\r\ncommonName             = Common Name (Domain Name)\r\nemailAddress           = Email Address\r\n\r\n[ v3_req ]\r\n\r\n# Extensions to add to a certificate request\r\nbasicConstraints       = CA:FALSE\r\nkeyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign\r\n"
  },
  {
    "path": "content/https-server/ssl/make-crt.sh",
    "content": "#!/bin/bash\r\n \r\necho \"\"\r\necho \"Generate a self signed certificate:\"\r\necho \"Enter certificate name (example: crt.crt):\"\r\nread crt_path\r\necho \"Enter path to certificate configuration file (example: crt.cnf):\"\r\nread cnf_path\r\necho \"Enter path to private key file (example: key.key):\"\r\nread key_path\r\necho \"Enter path to CSR file (example: csr.csr):\"\r\nread csr_path\r\necho \"Enter number of days until expiration (example: 365):\"\r\nread days\r\necho \"\"\r\n\r\n# certificate extensions must be stored in the v3_ca section in the configuration file\r\nif openssl x509 -req -days $days -in $csr_path -signkey $key_path -out $crt_path -extfile $cnf_path -extensions v3_ca\r\nthen\r\n\r\n  echo \"\"\r\n  echo \"Created \"$crt_path\" in \"$PWD\"/\"$crt_path\r\n\r\nelse\r\n\r\n  echo \"\"\r\n  echo \"Could not generate certificate due to error!\"\r\n\r\nfi\r\n\r\necho \"\"\r\n"
  },
  {
    "path": "content/https-server/ssl/make-csr.sh",
    "content": "#!/bin/bash\r\n \r\necho \"\"\r\necho \"Generate a Certificate Signing Request (CSR) file:\"\r\necho \"Enter CSR name (example: csr.csr):\"\r\nread csr_path\r\necho \"Enter path to CSR configuration file (example: csr.cnf):\"\r\nread cnf_path\r\necho \"Enter path to private key file (example: key.key):\"\r\nread key_path\r\necho \"\"\r\n\r\nif openssl req -config $cnf_path -new -key $key_path -inform PEM -out $csr_path -outform PEM\r\nthen\r\n\r\n  echo \"\"\r\n  echo \"Created \"$csr_path\" in \"$PWD\"/\"$csr_path\r\n\r\nelse\r\n\r\n  echo \"\"\r\n  echo \"Could not generate CSR due to error!\"\r\n\r\nfi\r\n\r\necho \"\"\r\n"
  },
  {
    "path": "content/https-server/ssl/make-key.sh",
    "content": "#!/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\nread key_path\r\necho \"\"\r\n\r\nif  openssl genpkey -algorithm RSA -out $key_path -outform PEM -pkeyopt rsa_keygen_bits:4096\r\nthen\r\n\r\n  echo \"\"\r\n  echo \"Created \"$key_path\" in \"$PWD\"/\"$key_path\r\n\r\nelse\r\n\r\n  echo \"\"\r\n  echo \"Could not create \"$key_path\" due to error!\"\r\n\r\nfi\r\n\r\necho \"\"\r\n"
  },
  {
    "path": "content/https-server/ssl/make-pfx.sh",
    "content": "#!/bin/bash\r\n\r\necho \"\"\r\necho \"Generate a Public Key Cryptography Standards # 12 ( PKCS#12 ) file:\"\r\necho \"Enter certificate name (example: crt.pfx):\"\r\nread pfx_path\r\necho \"Enter path to private key file (example: key.key):\"\r\nread key_path\r\necho \"Enter path to certificate file (example: crt.crt):\"\r\nread crt_path\r\necho \"\"\r\n\r\nif openssl pkcs12 -export -out $pfx_path -inkey $key_path -in $crt_path\r\nthen\r\n\r\n  echo \"\"\r\n  echo \"Created \"$pfx_path\" in \"$PWD\"/\"$pfx_path\r\n\r\nelse\r\n\r\n  echo \"\"\r\n  echo \"Could not create \"$pfx_path\" due to error!\"\r\n\r\nfi\r\n\r\necho \"\"\r\n"
  },
  {
    "path": "content/indexed-db/index.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#ffffff;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-content:space-around;\r\n  justify-items:space-around;\r\n  min-height:100%;\r\n  padding:8px;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\n#button-container {\r\n\r\n  align-content:space-around;\r\n  display:grid;\r\n  grid-template-columns:auto auto auto auto;\r\n  grid-template-rows:auto;\r\n  justify-content:space-around;\r\n  padding:16px 0;\r\n  width:100%;\r\n\r\n}\r\n\r\na {\r\n\r\n  align-content:center;\r\n  border-color:#c3daed;\r\n  border-radius:8px;\r\n  border-style:solid;\r\n  border-width:2px;\r\n  cursor:pointer;\r\n  display:grid;\r\n  font-size:1.5em;\r\n  font-weight:800;\r\n  height:64px;\r\n  justify-content:center;\r\n  user-select:none;\r\n  width:64px;\r\n\r\n}\r\n\r\np {\r\n\r\n  justify-self:center;\r\n  font-size:1.1em;\r\n  max-width:320px;\r\n  text-align:justify;\r\n\r\n}\r\n\r\nspan {\r\n\r\n  font-weight:800;\r\n  font-size:1.1em;\r\n\r\n}\r\n"
  },
  {
    "path": "content/indexed-db/index.html",
    "content": "<!DOCTYPE html>\r\n\r\n<!-- Here's a great resource: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB -->\r\n\r\n<html>\r\n\r\n  <head>\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>Indexed DB Test</title>\r\n\r\n    <link href = \"index.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog<br>Indexed DB</h1>\r\n\r\n    <p>Welcome to the example! Click on a button to change the background color, and Indexed DB will save your color choice to your browser cache. When you refresh the page, you will be greeted by the color you chose!</p>\r\n\r\n    <div id = \"button-container\">\r\n\r\n      <a style = \"background-color:#cc181e\">R</a>\r\n      <a style = \"background-color:#559900\">G</a>\r\n      <a style = \"background-color:#2793e8\">B</a>\r\n      <a style = \"background-color:rgba(0, 0, 0, 0.5)\">X</a>\r\n\r\n    </div>\r\n\r\n    <p>This example also keeps track of the number of button presses, which is <span id = \"presses\">0</span>, and the last time you visited the page, which was <span id = \"date\"></span>.</p>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      (function() {\r\n\r\n        // all the variables to run our database\r\n        var database, idb_request;\r\n\r\n        // request to open the specified database by name and version number\r\n        // if version number changes, the database is updated\r\n        idb_request = window.indexedDB.open(\"indexed-db\", 1);\r\n\r\n        // if there is an error, tell the user\r\n        idb_request.addEventListener(\"error\", function(event) {\r\n\r\n          alert(\"Could not open Indexed DB due to error: \" + this.errorCode);\r\n\r\n        });\r\n\r\n        /* if the database you specified cannot be found or the version number\r\n        is old, you will need an upgrade to create the new database schema */\r\n        idb_request.addEventListener(\"upgradeneeded\", function(event) {\r\n\r\n          /* Here we create a new object store called data, and give it an auto-\r\n          generated key path */\r\n          var storage = this.result.createObjectStore(\"data\", { autoIncrement: true });\r\n\r\n          // add an object to the \"data\" objectStore with the key, \"save-data\"\r\n          storage.add({ color: \"#2793e8\", date: \"not found.\", presses: \"0\" }, \"save-data\");\r\n\r\n          alert(\"Creating a new database!\");\r\n\r\n        });\r\n\r\n        // if you successfully open the database use this callback function\r\n        idb_request.addEventListener(\"success\", function(event) {\r\n\r\n          database = this.result;// store the database for later use\r\n\r\n          // now we are going to use some data from our database\r\n          var storage = database.transaction(\"data\", \"readwrite\").objectStore(\"data\");\r\n\r\n          storage.get(\"save-data\").addEventListener(\"success\", function(event) {\r\n\r\n            background = document.body.style.backgroundColor = this.result.color;\r\n            document.getElementById(\"date\").innerHTML = this.result.date;\r\n            presses = document.getElementById(\"presses\").innerHTML = this.result.presses;\r\n\r\n            this.result.date = new Date().toString();\r\n\r\n            storage.put(this.result, \"save-data\");\r\n\r\n          });\r\n\r\n          alert(\"Successfully opened database!\");\r\n\r\n        });\r\n\r\n        // all the variables to run our application\r\n        var buttons, background, presses;\r\n\r\n        // get the array of buttons, which are just a elements:\r\n        buttons = document.querySelectorAll(\"a\");\r\n\r\n        // set presses equal to zero (this will be reset if our database loads):\r\n        presses = 0;\r\n\r\n        // loop through all the buttons:\r\n        for (let index = buttons.length - 1; index > -1; -- index) {\r\n\r\n          // add a click listener to each button:\r\n          buttons[index].addEventListener(\"click\", function(event) {\r\n\r\n            // Clear the database if the X button is pressed:\r\n            if (database && this.innerHTML == \"X\") {\r\n\r\n              window.indexedDB.deleteDatabase(\"indexed-db\");\r\n\r\n              database = undefined;\r\n\r\n              // set up html for the white screen after deleting database\r\n              document.getElementById(\"button-container\").style.visibility = \"hidden\";\r\n              document.querySelector(\"h1\").innerHTML = \"You just deleted the database! Refresh the page to create a new one.\";\r\n              document.querySelector(\"h1\").style = \"color:\" + background;\r\n              document.body.style.backgroundColor = \"#ffffff\";\r\n\r\n              return;\r\n\r\n            } else if (database) { // if the database was established\r\n\r\n              presses ++;\r\n\r\n              // when a button is clicked, store its background color for saving:\r\n              background = this.style.backgroundColor;\r\n              // change the background color of the page to the button's color:\r\n              document.body.style.backgroundColor = background;\r\n\r\n              document.getElementById(\"presses\").innerHTML = presses;\r\n\r\n              // save the new data to the database in the objectStore, \"data\"\r\n              var storage = database.transaction(\"data\", \"readwrite\").objectStore(\"data\");\r\n              // get returns the object pointed to by the key, \"save-data\"\r\n              storage.get(\"save-data\").addEventListener(\"success\", function(event) {\r\n\r\n                // this.result is the object \"save-data\" was pointing to\r\n                this.result.color = background;\r\n                this.result.presses = presses;\r\n\r\n                // put writes the changed object back to the \"data\" objectStore\r\n                storage.put(this.result, \"save-data\");\r\n\r\n              });\r\n\r\n            }\r\n\r\n          });\r\n\r\n        }\r\n\r\n      })();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/inheritance/inheritance.html",
    "content": "<!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>Inheritance</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <script src = \"inheritance.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/inheritance/inheritance.js",
    "content": "// 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  Human.call(this, name);\r\n\r\n  this.job = job;\r\n\r\n}\r\n\r\n\r\nvar human = new Human(\"Tim\");\r\nvar worker = new Worker(\"John\", \"desk jockey\");\r\n\r\n\r\nconsole.log(human.name);\r\nconsole.log(worker.name + \", \" + worker.job);\r\n"
  },
  {
    "path": "content/inventory/inventory.html",
    "content": "<!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      * { margin:0; padding:0; }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      body { background-color:#000000; display:grid; }\r\n\r\n      canvas { align-self:center; justify-self:center; }\r\n\r\n    </style>\r\n\r\n    <title>Inventory</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Sprite {\r\n\r\n        constructor(x, y, sx, sy, w, h) {\r\n\r\n          this.x = x; this.y = y; this.sx = sx; this.sy = sy; this.w = w; this.h = h;\r\n\r\n          this.vx = this.vy = 0;\r\n\r\n        }\r\n\r\n        collidePoint(point) {\r\n\r\n          if (point.x < this.x || point.x > this.x + this.w || point.y < this.y || point.y > this.y + this.h) return false;\r\n\r\n          return true;\r\n\r\n        }\r\n\r\n        collideRect(sprite) {\r\n          \r\n          if (this.x > sprite.x + sprite.w || this.x + this.w < sprite.x || this.y > sprite.y + sprite.h || this.y + this.h < sprite.y) return false;\r\n\r\n          return true;\r\n              \r\n        }\r\n\r\n        updatePosition(gravity, friction, floor) {\r\n\r\n          this.vy += gravity;\r\n\r\n          this.x += this.vx;\r\n          this.y += this.vy;\r\n\r\n          this.vx *= friction;\r\n          this.vy *= friction;\r\n\r\n          if (this.y + this.h > floor) {\r\n\r\n            this.y = floor - this.h;\r\n            this.vy = 0;\r\n\r\n          }\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Inventory {\r\n\r\n        constructor(x, y, w, h) {\r\n\r\n          this.x = x; this.y = y; this.w = w; this.h = h;\r\n          this.color = \"rgba(255, 255, 255, 0.5)\";\r\n          this.items = new Array();\r\n          this.columns = 4;\r\n\r\n        }\r\n\r\n        addItem(sprite) {\r\n          \r\n          if (this.items.length == this.columns) {\r\n\r\n            sprite.vy = -4;\r\n            return false;\r\n\r\n          }\r\n\r\n          this.items.push(sprite)\r\n\r\n          return true;\r\n              \r\n        }\r\n\r\n        dropItem(index, x, y) {\r\n\r\n          var item = this.items[index];\r\n\r\n          if (item) {\r\n\r\n            this.items.splice(index, 1);\r\n            item.x = x;\r\n            item.y = y - item.h;\r\n            item.vx = Math.random() * 2 - 1;\r\n            item.vy = Math.random() * -5 - 1;\r\n\r\n          } return item;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      Inventory.prototype.collidePoint = Sprite.prototype.collidePoint;\r\n\r\n      var tile_set = new Image();\r\n      var tile_size = 16;\r\n      var map_columns = 16;\r\n      var map_rows = 12;\r\n      var map_ratio = map_columns / map_rows;\r\n      var map_floor = 148;\r\n      var map_friction = 0.9;\r\n      var map_gravity = 1;\r\n      var map_scale = 1;\r\n      var map = [ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,\r\n                  9, 9, 9,10,11,12, 9, 9,10,11,12, 9, 9, 9, 9, 9,\r\n                  9, 9,10, 5, 0, 0,12,10, 5, 0, 0, 9, 9, 9, 9, 9,\r\n                  9, 9, 4, 0, 0, 0, 0, 4, 0, 0, 0,12, 9, 9, 9, 9,\r\n                  9,10, 5, 0,11, 5, 0, 5, 0, 5, 0, 0, 9, 9, 9, 9,\r\n                 12, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,12, 9, 9, 9,\r\n                  0, 5, 0, 4, 0, 3, 0, 9, 9, 4, 0, 2, 0,12, 9, 9,\r\n                  0, 2, 0, 5, 0, 5, 0,11,11, 5, 0, 5, 0, 0,11,11,\r\n                  0, 2, 3, 0, 0, 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 2,\r\n                  8, 6, 7, 8, 8, 8, 8, 7, 8, 8, 8, 7, 6, 8, 8, 7,\r\n                  1, 1, 1, 1,13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,13,\r\n                  8, 8, 8, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7];\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\", { alpha: false });\r\n      var buffer = document.createElement(\"canvas\").getContext(\"2d\", { alpha: false });\r\n\r\n      var screen_h = document.documentElement.clientHeight - 16;\r\n      var screen_w = document.documentElement.clientWidth - 16;\r\n\r\n      var player = new Sprite(100, 100, 112, 16, 16, 32);\r\n      var inventory = new Inventory(120, 8, 128, 32);\r\n      var items = [new Sprite(Math.random() * 240, 100, 96, 16, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 0, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 16, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 32, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 48, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 64, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 80, 32, tile_size, tile_size),\r\n                   new Sprite(Math.random() * 240, 100, 96, 32, tile_size, tile_size)];\r\n\r\n      var pointer = { x:100, y:0, down:false };\r\n\r\n      function loop(time_step) {\r\n\r\n        window.requestAnimationFrame(loop);// perpetuate the loop\r\n\r\n        // Click inventory\r\n        if (pointer.down && inventory.items.length > 0 && inventory.collidePoint(pointer)) {\r\n          \r\n          let index = Math.floor((pointer.x - inventory.x) / (inventory.w / inventory.columns));\r\n\r\n          pointer.x = player.x + player.w * 0.5;\r\n\r\n          let item = inventory.dropItem(index, player.x, player.y);\r\n\r\n          if (item) items.unshift(item);\r\n\r\n        }\r\n\r\n        /* Draw the map */\r\n        for (let index = map.length - 1; index > -1; -- index) {\r\n\r\n          let value = map[index];\r\n          let tile_x = (index % map_columns) * tile_size;\r\n          let tile_y = Math.floor(index / map_columns) * tile_size;\r\n\r\n          buffer.drawImage(tile_set, (value % 8) * tile_size, Math.floor(value / 8) * tile_size, tile_size, tile_size, tile_x, tile_y, tile_size, tile_size);\r\n\r\n        }\r\n\r\n        // update player\r\n        player.vx = (pointer.x - player.x - tile_size * 0.5) * 0.025;\r\n        player.updatePosition(map_gravity, map_friction, map_floor);\r\n\r\n        /* Draw the player */\r\n        buffer.drawImage(tile_set, player.sx, player.sy, player.w, player.h, Math.round(player.x), Math.round(player.y), player.w, player.h);\r\n\r\n        var front_item_index = undefined;// Index of item at very front of screen \r\n         /* Draw the items */\r\n        for (let index = items.length - 1; index > -1; -- index) {\r\n\r\n          let item = items[index];\r\n\r\n          if (item.y + item.h >= map_floor && item.collideRect(player) && pointer.down && item.collidePoint(pointer)) {\r\n              \r\n            front_item_index = index;// store the frontmost item index\r\n\r\n          }\r\n\r\n          item.updatePosition(map_gravity, map_friction, map_floor);\r\n\r\n          buffer.drawImage(tile_set, item.sx, item.sy, item.w, item.h, Math.round(item.x), Math.round(item.y), item.w, item.h);\r\n\r\n        }\r\n\r\n        // If there is an item selected, add it to the inventory\r\n        if (front_item_index != undefined && inventory.addItem(items[front_item_index])) items.splice(front_item_index, 1); \r\n\r\n        /* Draw the inventory */\r\n        buffer.fillStyle = inventory.color;\r\n        buffer.fillRect(inventory.x, inventory.y, inventory.w, inventory.h);\r\n\r\n        for (let index = inventory.items.length - 1; index > -1; -- index) {\r\n\r\n          let item = inventory.items[index];\r\n          let dest_x = inventory.x + index * tile_size * 2;\r\n            \r\n          buffer.drawImage(tile_set, item.sx, item.sy, item.w, item.h, dest_x, inventory.y, tile_size * 2, tile_size * 2);\r\n\r\n        }\r\n\r\n        /* Draw to the display context */\r\n        context.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        pointer.down = false;\r\n\r\n      }\r\n\r\n      buffer.canvas.height = map_rows * tile_size;\r\n      buffer.canvas.width  = map_columns * tile_size;\r\n      buffer.imageSmoothingEnabled = false;\r\n\r\n      tile_set.addEventListener(\"load\", (event) => { loop(); });\r\n      tile_set.src = \"inventory.png\";\r\n\r\n      // IOS may not recognize click events on window or canvas elements.\r\n      window.addEventListener(\"click\", event => {\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = (event.clientX - rect.left) / map_scale;\r\n        pointer.y = (event.clientY - rect.top) / map_scale;\r\n        pointer.down = true;\r\n\r\n      });\r\n\r\n      // touchend event handler should cover ios, but now we have two listeners active on Android...\r\n      window.addEventListener(\"touchend\", event => {\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = (event.targetTouches[0].clientX - rect.left) / map_scale;\r\n        pointer.y = (event.targetTouches[0].clientY - rect.top) / map_scale;\r\n        pointer.down = true;\r\n\r\n      });\r\n\r\n      function resize(event) {\r\n\r\n        screen_h = document.documentElement.clientHeight - 16;// Get screen height and width\r\n        screen_w = document.documentElement.clientWidth - 16;\r\n        \r\n        if (screen_w / screen_h < map_ratio) { // fit map to screen\r\n\r\n            context.canvas.width = screen_w;\r\n            context.canvas.height = screen_w / map_ratio;\r\n\r\n        } else {\r\n\r\n            context.canvas.width = screen_h * map_ratio;\r\n            context.canvas.height = screen_h;\r\n\r\n        }\r\n\r\n        context.imageSmoothingEnabled = false;\r\n\r\n        map_scale = context.canvas.height / (map_rows * tile_size);\r\n\r\n      }\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      resize();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/ipo/components/input.js",
    "content": "// 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(update) {\r\n\r\n  this.handleClick = function(event) {\r\n\r\n    update();\r\n\r\n  };\r\n\r\n};\r\n\r\nInput.prototype = {\r\n\r\n  constructor:Input\r\n\r\n};\r\n"
  },
  {
    "path": "content/ipo/components/main.js",
    "content": "// 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 together. Since this application is extremely simple, using MVC or IPO is probably\r\nunnecessary. There are many ways to implement this approach, and MVC gets a bit\r\nmore involved than simply separating an application into three parts, but at the\r\ncore of the concept is organization, so if you are organizing your application even\r\nloosely into the three basic components, you are utilizing MVC */\r\n\r\n(function() {\r\n\r\n  function update() {\r\n\r\n    output.renderColor(processor.getRandomColor());\r\n\r\n  }\r\n\r\n  var input     = new Input(update);\r\n  var processor = new Processor();\r\n  var output    = new Output(document.body);\r\n\r\n  window.addEventListener(\"click\", input.handleClick);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/ipo/components/output.js",
    "content": "// Frank Poth 03/06/2018\r\n\r\n/* The output class handles everything to do with displaying graphics. */\r\n\r\nconst Output = function(element) {\r\n\r\n  this.element = element;\r\n\r\n  this.renderColor = function(color) {\r\n\r\n    this.element.style.backgroundColor = color;\r\n\r\n  }\r\n\r\n};\r\n\r\nOutput.prototype = {\r\n\r\n  constructor:Output\r\n\r\n};\r\n"
  },
  {
    "path": "content/ipo/components/processor.js",
    "content": "// Frank Poth 03/06/2018\r\n\r\n/* The processor class handles the application logic. */\r\n\r\nconst Processor = function() {\r\n\r\n  this.getRandomColor = function() {\r\n\r\n    return \"#\" + Math.floor(Math.random() * 16777215).toString(16);\r\n\r\n  }\r\n\r\n};\r\n\r\nProcessor.prototype = {\r\n\r\n  constructor:Processor,\r\n\r\n};\r\n"
  },
  {
    "path": "content/ipo/ipo.html",
    "content": "<!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</title>\r\n\r\n    <style>\r\n\r\n      * { box-sizing: border-box; margin:0; padding:0; user-select:none; word-break:break-word; word-wrap:break-word; }\r\n\r\n      html { height:100%; width:100%; }\r\n\r\n      body {\r\n\r\n        align-content:space-around;\r\n        background-color:#202830;\r\n        color:#ffffff;\r\n        display:grid;\r\n        justify-content:center;\r\n        min-height:100%;\r\n        text-align:center;\r\n        width:100%;\r\n\r\n      }\r\n\r\n      p { max-width:480px; text-align:justify; }\r\n\r\n    </style>\r\n\r\n    <script src = \"components/input.js\" type = \"text/javascript\"></script>\r\n    <script src = \"components/processor.js\" type = \"text/javascript\"></script>\r\n    <script src = \"components/output.js\" type = \"text/javascript\"></script>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - IPO<br><br>Input/Processing/Output</h1>\r\n\r\n    <p>Click the screen to change the background color!<br><br>IPO stands for Input/Processing/Output. IPO is a programming concept and organization technique much like MVC or Model/View/Controller. The concept is that most computer applications can be broken down into three basic components: taking user INPUT, PROCESSING that input, and OUTPUTing the final result. By separating the three components of a program, you can achieve more modular, maintainable code. Taking an Object Oriented approach to separating the components yields three classes that interact with each other via their public methods. Each class is totally self contained and has no internal references to any other components. It is modular because classes can be easily reused or replaced, and it is more maintainable because classes can be edited individually without disturbing other classes.</p>\r\n\r\n    <script src = \"components/main.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/json/json.html",
    "content": "<!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 = \"description\" content = \"Learn how to load and use JSON files with this working PoP Vlog example.\">\r\n    <title>PoP Vlog - Loading JSON</title>\r\n\r\n    <style>\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\n      html { height:100%; width:100%; }\r\n\r\n      body {\r\n\r\n        align-content:space-around;\r\n        align-items:space-around;\r\n        color:#ffffff;\r\n        background-color:#202830;\r\n        display:grid;\r\n        grid-template-columns:auto;\r\n        grid-template-rows:auto auto;\r\n        justify-items:center;\r\n        min-height:100%;\r\n        padding:16px;\r\n        width:100%;\r\n\r\n      }\r\n\r\n      h1 { font-size: 3.0em; }\r\n\r\n      p { font-family:monospace; font-size:1.25em; white-space:pre; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Loading JSON</h1>\r\n\r\n    <p></p>\r\n\r\n    <script src = \"json.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/json/json.js",
    "content": "// 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 strict\";\r\n\r\n  var load, p;// The load function loads our json file.\r\n\r\n  p = document.querySelector(\"p\");// The output p element.\r\n\r\n  load = function(url) {// Loads the file at the specified url.\r\n\r\n    var request;\r\n\r\n    request = new XMLHttpRequest();// We use an XMLHttpRequest to load the file.\r\n\r\n    /* This event listener will call the specified function when the file at the\r\n    specified url is loaded. */\r\n    request.addEventListener(\"readystatechange\", function(event) {\r\n\r\n      if (this.readyState == 4 && this.status == 200) {\r\n\r\n        // We parse the plain text response into a JavaScript object. */\r\n        var json = JSON.parse(this.responseText);\r\n\r\n        p.innerHTML = this.responseText;\r\n\r\n        // Now we can use the json object just like a regular JavaScript Object.\r\n        p.innerHTML += \"<br><br>json.array:         \" + json.array;\r\n        p.innerHTML += \"<br><br>json.number:        \" + json.number;\r\n        p.innerHTML += \"<br>json.number + 2:    \" + (json.number + 2);\r\n        p.innerHTML += \"<br><br>json.string:        \" + json.string;\r\n        p.innerHTML += \"<br><br>json.object.string: \" + json.object.string;\r\n\r\n      }\r\n\r\n    });\r\n\r\n    request.open(\"GET\", url);\r\n    request.send(null);\r\n\r\n  };\r\n\r\n  load(\"json.json\");// Our file is called json.json. Creative, I know.\r\n\r\n})();\r\n"
  },
  {
    "path": "content/json/json.json",
    "content": "{\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\n    \"string\":\"I'm a string from inside a JSON object!\"\r\n\r\n  }\r\n\r\n}\r\n"
  },
  {
    "path": "content/load-image/load-image.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto;\r\n  height:100%;\r\n  justify-items:center;\r\n  padding:0 8px;\r\n  width:100%;\r\n\r\n}\r\n"
  },
  {
    "path": "content/load-image/load-image.html",
    "content": "<!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 = \"description\" content = \"A working example of loading images in Javascript created by Frank Poth.\">\r\n\r\n    <link href = \"load-image.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <!-- The Document takes care of loading this image for us. But it might not\r\n    load before the script is executed. -->\r\n    <img src = \"flower.png\" style = \"display:none\">\r\n\r\n    <h1>PoP Vlog - Loading Images</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>This example simply loads images in three different ways.<br>The first image is loaded by the DOM.<br>The second image is loaded by setting its src attribute.<br>Finally, the third image is loaded with an XMLHttpRequest.</p>\r\n\r\n    <script src = \"load-image.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/load-image/load-image.js",
    "content": "// 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 and may not work.\r\n     load1 is an easy way to load an image, but it won't give you load progress events.\r\n     load2 is the most complex way to load an image, but it WILL give you load progress events. */\r\n  var buffer, display, images, load0, load1, load2, render, resize;\r\n\r\n  /* I use a buffer and a display canvas for easy scaling of the final image. */\r\n  buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n  display = document.querySelector(\"canvas\").getContext(\"2d\");\r\n  images = new Array();// This will hold our loaded images.\r\n\r\n  /* This isn't really even loading an image. The Document loads the image when\r\n  it parses the <img> tag in the HTML, then all we have to do in Javascript is get\r\n  a reference to that image. A problem with this method is that if the image loads\r\n  asynchronously, the script may execute before it finishes loading, meaning the\r\n  image you get will have a width and height of 0, which isn't very useful. This\r\n  method is really lazy, and probably shouldn't be used unless you know the image\r\n  has finished loading. */\r\n  load0 = function() {\r\n\r\n    images[0] = document.querySelector(\"img\");// There's only 1 img tag, so I use querySelector.\r\n\r\n    render();// Calling render draws the image\r\n\r\n  };\r\n\r\n  /* This is probably the most common method. It's simple and easy, and most importantly,\r\n  it works. Using a load event listener prevents you from getting progress reports, but\r\n  for a small web game this probably doesn't matter much unless you're really into\r\n  accurate loading screens. */\r\n  load1 = function() {\r\n\r\n    let image = new Image();// First we must create a new Image object.\r\n\r\n    /* We have to store the image and draw it whenever it loads, so let's make\r\n    an event handler for the load event. */\r\n    image.addEventListener(\"load\", function(event) {\r\n\r\n      /* When the image loads, we store it in the images array and draw it. */\r\n      images[1] = this;\r\n      render();\r\n\r\n    });\r\n\r\n    /* Setting the image's src will initiate loading, and eventually a load eventually\r\n    will fire and we can have access to our image. */\r\n    image.src = \"gelly.png\";// jelly or gelly? Hair gel... jelly sandwich... Huh.\r\n\r\n  };\r\n\r\n  /* This is the most complicated method, but has its benefits, like being able\r\n  to track load progress, and feeling cool for using XMLHttpRequest instead of\r\n  setting src. For some reason it just feels more like real loading. */\r\n  load2 = function() {\r\n\r\n    var request = new XMLHttpRequest();// Define your request.\r\n    var image = new Image();// Make a new empty image.\r\n\r\n    /* Now we make an event handler to do something with the array buffer we're\r\n    going to load. */\r\n    request.addEventListener(\"load\", function(event) {\r\n\r\n      /* First we convert our array buffer response to a Uint8Array. */\r\n      var bytes = new Uint8Array(this.response);\r\n      //alert(bytes);// You can actually see the width and height in this one.\r\n      /* Now we convert that numeric byte array to a string using fromCharCode. */\r\n      var string = String.fromCharCode.apply(null, bytes);\r\n      //alert(string);\r\n      /* Now we convert the string to a base64 string. */\r\n      var base64 = btoa(string);// Encode string\r\n      //alert(base64);\r\n      /* Finally we add the header to the base64 string to use with our png image. */\r\n      var data_url = \"data:image/png;base64,\" + base64;\r\n      //prompt(\"Copy data url?\", data_url);\r\n      /* Now we can set the src value directly. Setting it this way doesn't load anything,\r\n      and you have access to the useable image directly after setting src. */\r\n      /* 04/06/2018 looking back on this, it's not true. In fact, setting the src is\r\n      not synchronous even if you set it directly to a data_url. There may be a load\r\n      time which will cause any immediate requests for the image's data to fail because\r\n      the image has technically not loaded. */\r\n      image.src = data_url;\r\n      //alert(image.width);\r\n\r\n      images[2] = image;\r\n\r\n      render();\r\n\r\n    });\r\n\r\n    /* You can track the progress of the load with this event listener, but for a\r\n    16x16 image, this doesn't even have a chance to do anything. */\r\n    request.addEventListener(\"progress\", function(event) {\r\n\r\n      if (event.lengthComputable) {\r\n\r\n        console.log(\"loaded so far: \" + event.loaded + \" of \" + event.total);\r\n\r\n      }\r\n\r\n    });\r\n\r\n    request.open(\"GET\", \"human.png\");\r\n    request.responseType = \"arraybuffer\";// You must specify arraybuffer type!\r\n    request.send(null);\r\n\r\n  };\r\n\r\n  /* This renders the loaded images to the buffer and then to the display canvas. */\r\n  render = function() {\r\n\r\n    var x = 0;\r\n\r\n    buffer.fillStyle = \"#283038\";\r\n    buffer.fillRect(0, 0, buffer.canvas.width, buffer.canvas.height);\r\n\r\n    for (let index = images.length - 1; index > -1; -- index) {\r\n\r\n      let image = images[index];\r\n\r\n      buffer.drawImage(image, 0, 0, image.width, image.height, x, 0, image.width, image.height);\r\n\r\n      x += image.width;\r\n\r\n    }\r\n\r\n    /* Handles scaling of buffer to display as well. */\r\n    display.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, display.canvas.width, display.canvas.height);\r\n\r\n  };\r\n\r\n  /* Make sure everything fits nicely in the window, and redraws on screen resize events. */\r\n  resize = function(event) {\r\n\r\n    display.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n    if (display.canvas.width > document.documentElement.clientHeight) {\r\n\r\n      display.canvas.width = document.documentElement.clientHeight;\r\n\r\n    }\r\n\r\n    /* make sure we're maintaining aspect ratio. 1 image high, by 3 wide. */\r\n    display.canvas.height = display.canvas.width * (1/3);\r\n\r\n    display.imageSmoothingEnabled = false;// This keeps the image looking sharp.\r\n\r\n    render();\r\n\r\n\r\n  };\r\n\r\n  window.addEventListener(\"resize\", resize);\r\n\r\n  /* We have 3 16x16 images, so the buffer should fit them exactly. */\r\n  buffer.canvas.height = 16;\r\n  buffer.canvas.width = 48;\r\n\r\n  resize();\r\n\r\n  load0();\r\n  load1();\r\n  load2();\r\n\r\n  /* The code below sets the source of a new image directly to an inline base64 string.\r\n  This means no loading even occurs, the image data is just there. This is the data\r\n  from the human.png file. */\r\n  /* images[0] = new Image();\r\n  images[0].src = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAzQDNAM2UZCwLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QwXDhkbAgmtXgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAF3SURBVDjLfZO/S8NAFMc/VzqkkKGr4FC0Q8AlkM6C4Ogf4NC5imPt4OCm6Ohmq1NxcnBwCKJQFFdpMGMHfxUcHIQKFnrbOcQ7r03iQQjvve/3+773Hicqno99NhfeFTln5+pTzOJN4m0Qs7vi5pIB3JJD52VepAQ02S05ueTxRGaKFN8GcQqU1z2rXpgFtdqXtNqXuCUH25EmjyeS+tyTyRf1FWxQ2Vvla9AzIB13muspF8bB/t1YbB+d/zdDNg66AEIP3QhEUTQFtLtnxXu33+zoLURRRBAEpjiKQyWc7G2UvVUBGPKhUggIgMRBv4ECWNwKU+Tn4zUAaqfJFbhW0O1S1ABNBnDu18gxQb+Bqp0iGAJDKEDEKA5VtdlDWgOWkqlY56rNHqM4NM2MAyUl9QdfXCzHqnSW0TmA+oMvHqVMyK83uhTY9iBAEaBYsr7fXL9hKTZOkr/9uiqe/yegBQEjMnMqnj/9GpNdoXCAD/5eXUZO834AfkujNjeRqMcAAAAASUVORK5CYII=\";\r\n  render(); */\r\n\r\n})();\r\n"
  },
  {
    "path": "content/multiple-inheritance/multiple-inheritance.html",
    "content": "<!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>Inheritance</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <script src = \"multiple-inheritance.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/multiple-inheritance/multiple-inheritance.js",
    "content": "// 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() {\r\n\r\n    console.log(\"Hey, I'm a human and my name is \" + this.name);\r\n\r\n  }\r\n\r\n};\r\n\r\nfunction Worker(job) {\r\n\r\n  this.job = job;\r\n\r\n}\r\n\r\nWorker.prototype = {\r\n\r\n  work:function() {\r\n\r\n    console.log(\"I am a worker and my job is \" + this.job);\r\n\r\n  }\r\n\r\n};\r\n\r\nfunction Bob(job) {\r\n\r\n  Human.call(this, \"Bob\");\r\n  Worker.call(this, job);\r\n\r\n}\r\n\r\nBob.prototype = Object.create(Human.prototype);\r\nObject.assign(Bob.prototype, Worker.prototype);\r\n\r\nvar bob = new Bob(\"rocket ship captain\");\r\n\r\nbob.talk();\r\nbob.work();\r\n"
  },
  {
    "path": "content/objects-and-vars/objects.html",
    "content": "<!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 = \"objects.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/objects-and-vars/objects.js",
    "content": "\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_say) {\r\n\r\n    console.log(this.width + \", \" + what_to_say);\r\n\r\n  }\r\n\r\n};\r\n\r\nrectangle.print(\"I'm a rectangle!\");\r\n\r\nfunction sayHello() {\r\n\r\n  alert(\"Hello!\");\r\n\r\n}\r\n\r\nvar sayHello = function() {\r\n\r\n  alert(\"Hello, again!\");\r\n\r\n}\r\n\r\nsayHello();\r\n"
  },
  {
    "path": "content/offline-web-app/manifest.json",
    "content": "{\r\n\r\n  \"author\": \"PoP Vlog\",\r\n  \"background_color\": \"#000000\",\r\n  \"description\": \"A simple Progressive Web Application\",\r\n  \"display\": \"fullscreen\",\r\n\r\n  \"icons\": [\r\n\r\n    {\r\n\r\n      \"src\": \"web-app.png\",\r\n      \"sizes\": \"192x192\",\r\n      \"type\": \"image/png\"\r\n\r\n    }\r\n\r\n  ],\r\n\r\n  \"lang\": \"en-US\",\r\n  \"name\": \"Offline Web App\",\r\n  \"orientation\": \"portrait\",\r\n  \"scope\": \"./\",\r\n  \"short_name\": \"Web App\",\r\n  \"start_url\": \"web-app.html\",\r\n  \"theme_color\": \"#0099ff\",\r\n  \"version\": \"0.2\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/offline-web-app/server.js",
    "content": "// 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\"); // file system\r\n  https = require(\"https\"); // creates an https server\r\n  path = require(\"path\"); // used for working with url paths\r\n\r\n  // used to create response headers\r\n  /* If the user requests a .css file, we want to ensure we attach \"text/css\" to\r\n  our response header, this way the browser knows how to handle it. */\r\n  mimetypes = {\r\n\r\n    \"css\":\"text/css\",\r\n    \"html\":\"text/html\",\r\n    \"ico\":\"image/ico\",\r\n    \"jpg\":\"image/jpeg\",\r\n    \"js\":\"text/javascript\",\r\n    \"json\":\"application/json\",\r\n    \"png\":\"image/png\"\r\n\r\n  };\r\n\r\n  options = {\r\n\r\n    pfx: fs.readFileSync(\"ssl/crt.pfx\"),\r\n    passphrase: \"password\"\r\n\r\n  };\r\n\r\n  // Start a secure server that uses the credentials in ssl/crt.pfx\r\n  server = https.createServer(options, function(request, response) {\r\n\r\n    console.log(request.url);\r\n\r\n    /* When requesting the homepage of a website, we usually only type\r\n    www.mysite.com, but the server returns www.mysite.com/index.html. To make\r\n    it easier for users to access our site, we add \"/index.html\" to their url\r\n    so the user doesn't have to type out the whole address of our home page. */\r\n\r\n    // If the url is empty\r\n    if (request.url == \"\" || request.url == \"/\") {\r\n\r\n      // The user is requesting the home page of the website, so give it to them\r\n      request.url = \"web-app.html\";\r\n\r\n    }\r\n\r\n    // Next we read the file at the requested url and write it to the document.\r\n    /* __dirname is just the base directory of your website, so if your website\r\n    is www.coolsite.com, then __dirname is www.coolsite.com. When you put it all\r\n    together it looks like www.coolsite.com/index.html or whatever the requested\r\n    url is */\r\n    fs.readFile(__dirname + \"/\" + request.url, function(error, content) {\r\n\r\n      if (error) { // if there is an error reading the requested url\r\n\r\n        console.log(\"Error: \" + error); // output it to the console\r\n\r\n      } else { // else, there is no error, write the file contents to the page\r\n\r\n        // 200 is code for OK, and the second parameter is our content header\r\n        response.writeHead(200, {'Content-Type':mimetypes[path.extname(request.url).split(\".\")[1]]});\r\n        response.write(content); // write that content to our response object\r\n\r\n      }\r\n\r\n      response.end(); // This will send our response object to the browser\r\n\r\n    });\r\n\r\n  });\r\n\r\n  server.listen(\"2468\", \"192.168.0.101\", function() {\r\n\r\n    console.log(\"Server started!\");\r\n\r\n  }); // listen on 192.168.0.101:2468\r\n\r\n})();\r\n"
  },
  {
    "path": "content/offline-web-app/web-app-service.js",
    "content": "// Frank Poth 10/25/2017\r\n// here's a great resource on service workers: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers\r\n// here's where I basically copy and pasted the code for this service worker: https://airhorner.com/sw.js\r\n\r\nself.addEventListener(\"activate\",  function(event) {\r\n\r\n  event.waitUntil(self.clients.claim().then(function() {\r\n\r\n    self.skipWaiting();\r\n\r\n  }));\r\n\r\n});\r\n\r\nself.addEventListener(\"fetch\", function(event) {\r\n\r\n  event.respondWith(caches.match(event.request).then(function(response) {\r\n\r\n    if (response && response.ok) {\r\n\r\n      return response;\r\n\r\n    }\r\n\r\n  }));\r\n\r\n});\r\n\r\nself.addEventListener(\"install\", function(event) {\r\n\r\n  event.waitUntil(caches.open(\"web-app\").then(function(cache) {\r\n\r\n    return cache.addAll([ \"/\", \"manifest.json\", \"web-app.css\", \"web-app.html\", \"web-app.png\"]).then(function() {\r\n\r\n      self.skipWaiting();\r\n\r\n    });\r\n\r\n  }));\r\n\r\n});\r\n"
  },
  {
    "path": "content/offline-web-app/web-app.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  justify-items:space-around;\r\n  padding:8px;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\nimg {\r\n\r\n  margin:0 auto;\r\n\r\n}\r\n\r\np {\r\n\r\n  font-size:1.1em;\r\n  text-align:justify;\r\n\r\n}\r\n"
  },
  {
    "path": "content/offline-web-app/web-app.html",
    "content": "<!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 = \"description\" content = \"A simple web app.\">\r\n    <meta name = \"viewport\" content = \"width=device-width, initial-scale=1\">\r\n    <meta name = \"application-name\" content = \"Web App\">\r\n    <link href = \"manifest.json\" rel = \"manifest\">\r\n    <link href = \"web-app.png\" rel = \"icon\" type = \"image/png\">\r\n    <link href = \"web-app.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>Offline Android Web App!</h1>\r\n\r\n    <img src = \"web-app.png\">\r\n\r\n    <p>If you are viewing this app in full screen mode, you are witnessing a Progressive Web App. This app should meet the requirements to be installed to Android devices with the Add To Home Screen feature in Chrome browsers. It also tries to register a service worker which will cache files in the browser cache for use in offline mode!</p>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      if (\"serviceWorker\" in navigator) {\r\n\r\n        navigator.serviceWorker.register(\"./web-app-service.js\", { scope: \"./\" }).then(function(registration) {\r\n\r\n        }).catch(function(error) {\r\n\r\n          alert(\"Could not register service worker! This application will not be able to run in offline mode.\");\r\n          alert(error);\r\n\r\n        });\r\n\r\n      }\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/pagination/article1.txt",
    "content": "1. Pagination is a verb. To paginate content one must break that content into smaller\r\ngroups. For instance, if I had a database with 1,000 items in it and wanted to display\r\nthem to clients via a web page, I could make viewing the items more manageable by\r\nbreaking up the 1,000 items into groups of 10, 20, 50 or even 100. The\r\npractice of breaking large chunks of data into smaller, more manageable groups for\r\nviewing is known as pagination.\r\n"
  },
  {
    "path": "content/pagination/article2.txt",
    "content": "2. Pagination is used in many applications, perhaps without you even knowing it. If\r\nyou have used a search engine recently, you have used a web page employing pagination.\r\nSearch results are broken up into 10 to 20 items per page and at the bottom of that\r\npage there is usually a small navigator bar that you can use to view the next set\r\nof results if you wish to do so. Many paginators also allow the user to select a\r\nspecific page within several pages of the currently loaded content. Some paginators\r\nallow the user to input a specific page manually with the keyboard. Another common\r\nfeature of pagination is to allow the user to specify how many results are loaded\r\nper page.\r\n"
  },
  {
    "path": "content/pagination/article3.txt",
    "content": "3. The most common use case of pagination is to organize search results requested from\r\na database. Good examples of this are sites like Ebay and Craigslist. Social media\r\nsites like Facebook and Twitter also use a type of pagination to limit the number\r\nof results loaded per page in their news feeds or especially in photo albums. Because\r\nthis example is written for a static web server, it cannot access server side databases,\r\nmaking it rather uncommon. Some good uses for a static paginator are: photo albums,\r\narticles for blog sites, and custom links inside of a personal portfolio. Ambitious\r\nstatic site builders might even implement a search engine for their static website.\r\nKeep in mind, doing any of this on a static website requires a lot of additional\r\noverhead and maintenance for things that can be done easily with a dynamic server.\r\n"
  },
  {
    "path": "content/pagination/article4.txt",
    "content": "4. If you are planning to build a static website and want a simple paginator\r\nto help your users view photos or links, this paginator will work well. Keep in\r\nmind that it requires far more maintenance than a dynamic server. You will have\r\nto update the list of files to cycle through anytime you add new content you want\r\nto paginate to your website. If you are building a website that gets frequent updates, you\r\nshould seriously consider a hosting service that supports NodeJS. NodeJS is an easy\r\nto learn and use backend scripting language that writes like JavaScript. Of course\r\nthere are many great server languages to choose from, but NodeJS is a natural fit\r\nfor front end developers who want to write a simple server with minimal overhead.\r\n"
  },
  {
    "path": "content/pagination/article5.txt",
    "content": "5. Your static site can greatly benefit from AJAX and the XMLHttpRequest. This paginator\r\nrelies entirely on XMLHttpRequest to communicate with the static file server. Luckily,\r\nif you are building a static site and want dynamic content, a static file server\r\nwill allow you to use AJAX to request files. Any free hosting site will offer this\r\nfunctionality, so you can use AJAX to create awesome tools like this paginator for\r\nyour static site. To further improve the functionality of your static website, you\r\ncan use things like service workers and Indexed DB to give your users a great experience.\r\n"
  },
  {
    "path": "content/pagination/pagination.css",
    "content": "/* 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%; width:100%; }\r\n\r\nbody {\r\n\r\n  align-content:center;\r\n  align-items:center;\r\n  background-color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nh1 { color:#cc181e; font-size: 3.0em; font-weight:1000; margin:16px; text-align:center; }\r\n\r\n.paginator {\r\n\r\n  display:grid;\r\n  grid-row-gap:1.0em;\r\n  grid-template-areas:\"content\" \"navigator\";\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto;\r\n  max-height:480px;\r\n  max-width:480px;\r\n\r\n}\r\n\r\n.paginator-content {\r\n\r\n  color:#202830;\r\n  display:grid;\r\n  font-size:1.25em;\r\n  font-weight:800;\r\n  grid-area:content;\r\n  max-height:320px;\r\n  overflow-y:auto;\r\n  padding:8px;\r\n  text-align:justify;\r\n\r\n}\r\n\r\n.paginator-navigator {\r\n\r\n  align-items:center;\r\n  background-color:#cc181e;\r\n  border-radius:8px;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-area:navigator;\r\n  grid-template-columns:auto auto auto;\r\n  grid-template-rows:auto;\r\n  justify-content:center;\r\n  justify-items:center;\r\n  margin:0 auto;\r\n  padding:0 8px;\r\n  text-align:center;\r\n\r\n}\r\n\r\n.paginator-button {\r\n\r\n  cursor:pointer;\r\n  font-size:1.25em;\r\n  border-color:#ffffff;\r\n  padding:8px;\r\n  user-select:none;\r\n\r\n}\r\n\r\n.paginator-index {\r\n\r\n  font-size:1.5em;\r\n  font-weight:800;\r\n  user-select:none;\r\n\r\n}\r\n"
  },
  {
    "path": "content/pagination/pagination.html",
    "content": "<!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. -->\r\n    <link href = \"pagination.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \"description\" content = \"Learn how to implement pagination in pure JavaScript with this interactive example!\">\r\n\r\n    <title>PoP Vlog - Pagination</title>\r\n\r\n    <!-- First we must link in the paginator.js script file so we can use it in\r\n    the body of our document. -->\r\n    <script src = \"paginator.js\" type = \"text/javascript\"></script>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Pagination</h1>\r\n\r\n    <!-- To import the paginator and its content, we use the Paginator.create method.\r\n    Note that after this method is called, the script tag it was called from will\r\n    be replaced with the paginator's html, meaning people looking through your page's\r\n    source in a browser won't see the script tag below unless they disable JavaScript;\r\n    they'll only see markup for the paginator. Pretty sweet, right? -->\r\n    <script type = \"text/javascript\">Paginator.create([\"article1.txt\", \"article2.txt\", \"article3.txt\", \"article4.txt\", \"article5.txt\"], 0, 1);</script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/pagination/paginator.js",
    "content": "// Frank Poth 01/27/2018\r\n\r\n/* This paginator is designed for static websites, making it quite rare. People\r\ndon't usually do stuff like this for static websites because of how much maintenance\r\nit requires. Every time you add content to your site, you have to update the paginator.\r\nSites with backend servers that can handle loading dynamic content for your site at\r\na simple request are far better suited to handle this sort of thing. But, if you are\r\nset on making your static site as dynamic as possible and don't mind maintaining it,\r\nthis will work just fine for you. */\r\n\r\n/* The paginator takes an array of links or urls to content. Each url and its corresponding\r\nfile content represent 1 item to paginate. The index is the first item in that list\r\nof links to be displayed on a page, and the limit is how many items will be displayed\r\nper page. */\r\nconst Paginator = function(links, index, limit) {\r\n\r\n  this.links = links;\r\n  this.index = 0;\r\n  /* Make sure the limit per page is not greater than the actual amount of items we have or 0 */\r\n  this.limit = (limit <= links.length && limit > 0) ? limit : links.length;\r\n\r\n  /* I created my html with JavaScript, but you could easily do this with an HTML\r\n  template or create your HTML inline and use querySelector to get a reference to it.\r\n  I thought it would be more portable to use this approach. That being said, All the\r\n  CSS for these elements is in the pagination.css file. How you style this stuff is\r\n  up to you. Even without the css, it all works. */\r\n  this.html = document.createElement(\"div\");\r\n  this.html.setAttribute(\"class\", \"paginator\");\r\n  this.html.innerHTML = \"<div class = \\\"paginator-content\\\"></div><div class = \\\"paginator-navigator\\\"><a class = \\\"paginator-button\\\">back</a><div class = \\\"paginator-index\\\"></div><a class = \\\"paginator-button\\\">next</a></div>\";\r\n\r\n  /* Here we set up the buttons created above to respond to click events and give\r\n  them a reference to their paginator. */\r\n  var buttons = this.html.querySelectorAll(\"a\");\r\n\r\n  for (let index = buttons.length - 1; index > -1; -- index) {// Loop through all buttons.\r\n\r\n    let button = buttons[index];\r\n\r\n    button.addEventListener(\"click\", Paginator.click);\r\n    /* Button elements need access to their paginator object so they can access its\r\n    variables inside of the Paginator.click event handler. */\r\n    button.paginator = this;\r\n\r\n  }\r\n\r\n};\r\n\r\nPaginator.prototype = {\r\n\r\n  constructor:Paginator,\r\n\r\n  /* Changes the currently displayed items in the paginator's content div. */\r\n  changeIndex:function(new_index) {\r\n\r\n    var content_div, content_strings, limit, loaded;\r\n\r\n    this.index = new_index;// The new index in the list of links to start getting items from.\r\n\r\n    /* Show the users what page they are on and how many pages there are. */\r\n    this.html.querySelector(\".paginator-index\").innerHTML = (new_index / this.limit + 1) + \" of \" + Math.ceil(this.links.length / this.limit);\r\n\r\n    content_div = this.html.querySelector(\".paginator-content\");\r\n    content_div.scrollTop = 0;// Whenever the page changes, scroll content to the top.\r\n    content_strings = [];// We load each file in order into this array.\r\n    /* Make sure we don't try to load items that don't exist. */\r\n    limit = (new_index + this.limit <= this.links.length) ? this.limit : this.links.length - new_index;\r\n    loaded = 0;// Keep track of how many files have been loaded.\r\n\r\n    paginator = this;// Get a reference to this paginator.\r\n\r\n    /* The reason for this seemingly convoluted method of loading content is that\r\n    asynchronous requests can load at different rates, meaning if I request content\r\n    in a specific order I might get it in a different order. This would be terrible\r\n    if I were requesting pages in a book and they came back out of order, so to remedy\r\n    this, I load each file's content into the content_strings array in the correct\r\n    order and then when everything is done loading, I put the strings together. */\r\n    for (let index = 0; index < limit; index ++) {\r\n\r\n      Paginator.requestContent(this.links[this.index + index], function(request) {\r\n\r\n        loaded ++;\r\n\r\n        // index is relative to the value it was set to in the encompassing for loop thanks to let scope.\r\n        content_strings[index] = \"<br>\" + request.responseText + \"<br>\";\r\n\r\n        if (loaded >= limit) {\r\n\r\n          content_div.innerHTML = \"\";\r\n\r\n          for (let index = 0; index < limit; index ++) {\r\n\r\n            content_div.innerHTML += content_strings[index];\r\n\r\n          }\r\n\r\n        }\r\n\r\n      });\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\n/* The click listener for this paginator. */\r\nPaginator.click = function(event) {\r\n\r\n  var shift;// The number of items to shift past when loading the items for the new page.\r\n\r\n  switch(this.innerHTML) {\r\n\r\n    case \"back\":\r\n\r\n      shift = this.paginator.index - this.paginator.limit;\r\n      if (shift < 0) return;\r\n\r\n    break;\r\n\r\n    case \"next\":\r\n\r\n      shift = this.paginator.index + this.paginator.limit;\r\n      if (shift >= this.paginator.links.length) return;\r\n\r\n    break;\r\n\r\n  }\r\n\r\n  this.paginator.changeIndex(shift);\r\n\r\n};\r\n\r\n/* Creates a paginator inside a <script> tag and replaces that tag with the paginator's html. */\r\nPaginator.create = function(links, index, limit) {\r\n\r\n  var paginator, script;\r\n\r\n  script = document.currentScript;// Get the currently running script.\r\n\r\n  paginator = new Paginator(links, 0, limit);\r\n\r\n  paginator.changeIndex(index);/* Load up the content. */\r\n\r\n  /* Replace the running script with the html of the paginator object. */\r\n  script.parentNode.replaceChild(paginator.html, script);\r\n\r\n};\r\n\r\n/* Loads a file at the specified url and executes the callback. */\r\nPaginator.requestContent = function(url, callback) {\r\n\r\n  var request;\r\n\r\n  request = new XMLHttpRequest();\r\n\r\n  request.addEventListener(\"load\", function(event) {\r\n\r\n    callback(this);\r\n\r\n  });\r\n\r\n  request.open(\"GET\", url);\r\n  request.send(null);\r\n\r\n}\r\n"
  },
  {
    "path": "content/particle-pool/particle-pool.html",
    "content": "<!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 = \"description\" content = \"Take a look at the source code for this interactive HTML5 and JavaScript program that utilizes a particle pool!\">\r\n    <title>Pool</title>\r\n\r\n    <style>\r\n      \r\n      * { margin:0; padding:0; overflow:hidden; pointer-events:none; user-select:none; }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      body { background-color:#000000; cursor:pointer; display:grid; }\r\n\r\n      canvas { align-self:center; justify-self:center; }\r\n\r\n      p { color:#000000; font-size:1.25em; font-weight:bold; position:fixed; top:16px; left:16px; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n  \r\n  <body>\r\n\r\n    <canvas id = \"canvas\"></canvas>\r\n    <p id = \"p\"></p>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Particle {\r\n\r\n        constructor(x, y, radius, rgb_string) {\r\n\r\n          this.radius = radius;\r\n          this.reset(x, y, rgb_string);\r\n\r\n        }\r\n\r\n        reset(x, y, rgb_string) {\r\n\r\n          this.x = x;\r\n          this.y = y;\r\n          this.vx = Math.random() * 1 - 0.5;\r\n          this.vy = Math.random() * 1 - 0.5;\r\n          this.rgb_string = rgb_string;\r\n          this.a = 1;\r\n\r\n        }\r\n\r\n        get color() {\r\n\r\n          return \"rgba(\" + this.rgb_string + \",\" + this.a + \")\";\r\n\r\n        }\r\n\r\n        updatePosition() {\r\n\r\n          this.a -= 0.01;\r\n\r\n          this.x += this.vx;\r\n          this.y += this.vy;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Color {\r\n\r\n        constructor(r, g, b) {\r\n\r\n          this.r = r; this.g = g; this.b = b;\r\n\r\n        }\r\n\r\n        getRGBString() {\r\n\r\n          return String(this.r + \",\" + this.g + \",\" + this.b);\r\n\r\n        }\r\n\r\n        gradualShift(direction) {\r\n\r\n          this.r = Math.floor(Math.abs(Math.cos(direction * 0.75) * 256));\r\n          this.g = Math.floor(Math.abs(Math.sin(direction * 0.25) * 256));\r\n          this.b = Math.floor(Math.abs(Math.sin(direction * 0.5) * 256));\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var pool      = new Array();\r\n      var particles = new Array();\r\n    \r\n      var context = document.getElementById(\"canvas\").getContext(\"2d\", { alpha:false });\r\n      var output = document.getElementById(\"p\");\r\n\r\n      var pointer = { x:0, y:0, down:false };\r\n      var color = new Color(0, 0, 0);\r\n      var direction = 0;\r\n\r\n      function loop(time_stamp) {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        direction += 0.01;\r\n        color.gradualShift(direction);\r\n\r\n        output.style.color = \"rgb(\" + color.getRGBString() + \")\";\r\n        document.body.style.backgroundColor = output.style.color;\r\n\r\n        if (pointer.down) {\r\n\r\n          for (let index = 0; index < 2; ++ index) {\r\n            let particle = pool.pop();\r\n\r\n            if (particle != undefined) {\r\n\r\n              particle.reset(pointer.x, pointer.y, color.getRGBString());\r\n              particles.push(particle);\r\n\r\n            } else {\r\n\r\n              particles.push(new Particle(pointer.x, pointer.y, Math.floor(Math.random() * 10 + 10), color.getRGBString()));\r\n\r\n            }\r\n          }\r\n\r\n        }\r\n\r\n        for (let index = particles.length - 1; index > -1; -- index) {\r\n\r\n          let particle = particles[index];\r\n\r\n          particle.updatePosition();\r\n\r\n          if (particle.a <= 0) pool.push(particles.splice(index, 1)[0]);\r\n\r\n          context.beginPath();\r\n          context.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);\r\n          context.fillStyle = particle.color;\r\n          context.fill();\r\n          context.closePath();\r\n\r\n        }\r\n\r\n        output.innerHTML = \"pool: \" + pool.length + \"<br>live: \" + particles.length;\r\n\r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n        context.canvas.width  = document.documentElement.clientWidth  - 16;\r\n\r\n      }\r\n\r\n      function mouseDownMoveUp(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - rect.left;\r\n        pointer.y = event.clientY - rect.top;\r\n\r\n        switch(event.type) {\r\n\r\n          case \"mousedown\": pointer.down = true; break;\r\n          case \"mouseup\":   pointer.down = false;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function touchEndMoveStart(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n        var touch = event.targetTouches[0];\r\n\r\n        if (touch) {\r\n          pointer.x = touch.clientX - rect.left;\r\n          pointer.y = touch.clientY - rect.top;\r\n        }\r\n\r\n        switch(event.type) {\r\n\r\n          case \"touchstart\": pointer.down = true; break;\r\n          case \"touchend\": pointer.down = false;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      window.addEventListener(\"mousedown\", mouseDownMoveUp);\r\n      window.addEventListener(\"mousemove\", mouseDownMoveUp);\r\n      window.addEventListener(\"mouseup\"  , mouseDownMoveUp);\r\n\r\n      window.addEventListener(\"touchend\", touchEndMoveStart);\r\n      window.addEventListener(\"touchmove\", touchEndMoveStart);\r\n      window.addEventListener(\"touchstart\", touchEndMoveStart);\r\n\r\n      resize();\r\n      loop();\r\n    \r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/platform/platform.html",
    "content": "<!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\thtml, body { height:100%; width:100%; }\r\n\r\n\t\t\tbody { background-color:#000000; display:grid; }\r\n\r\n\t\t\tcanvas { align-self:center; justify-self:center; }\r\n\r\n\t\t</style>\r\n\r\n\t</head>\r\n\r\n\t<body>\r\n\r\n\t\t<canvas></canvas>\r\n\r\n\t\t<script type = \"text/javascript\">\r\n\r\n\t\t\tconst Controller = function() {\r\n\r\n\t\t\t\tthis.left = this.right = this.up = false;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tController.prototype.keyDownUp = function(event) {\r\n\r\n\t\t\t\tvar down = event.type == \"keydown\" ? true : false;\r\n\r\n\t\t\t\tswitch(event.keyCode) {\r\n\r\n\t\t\t\t\tcase 37: this.left = down; break;\r\n\t\t\t\t\tcase 38: this.up = down; break;\r\n\t\t\t\t\tcase 39: this.right = down; break;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t};\r\n\r\n\t\t\tconst Player = function(x, y, source_x, source_w, behavior) {\r\n\r\n\t\t\t\tthis.source_x = source_x;\r\n\t\t\t\tthis.source_w = source_w;\r\n\t\t\t\tthis.behavior = behavior;\r\n\r\n\t\t\t\tthis.ox = this.x = x;\r\n\t\t\t\tthis.oy = this.y = y;\r\n\t\t\t\tthis.vx = this.vy = 0;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tPlayer.prototype = {\r\n\r\n\t\t\t\tbehave:function() { this.behavior(this); },\r\n\r\n\t\t\t};\r\n\r\n\t\t\tconst Dude = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 240, 16, behavior);\r\n\r\n\t\t\t\tthis.jumping = false;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tObject.assign(Dude.prototype, Player.prototype);\r\n\r\n\t\t\tfunction dudeBehavior(dude) {\r\n\r\n\t\t\t\tif (!dude.jumping && controller.up) {\r\n\r\n\t\t\t\t\tcontroller.up = false;\r\n\t\t\t\t\tdude.vy -= 10;\r\n\t\t\t\t\tdude.jumping = true;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (controller.left) dude.vx -= 0.75;\r\n\t\t\t\tif (controller.right) dude.vx += 0.75;\r\n\r\n\t\t\t\tvar airborne = true;\r\n\r\n\t\t\t\tdude.vy += gravity;\r\n\r\n\t\t\t\tdude.oy = dude.y;\r\n\t\t\t\tdude.ox = dude.x;\r\n\t\t\t\tdude.y += dude.vy;\r\n\t\t\t\tdude.x += dude.vx;\r\n\r\n\t\t\t\tif (dude.y > floor) {\r\n\r\n\t\t\t\t\tairborne = false;\r\n\t\t\t\t\tdude.y = floor;\r\n\t\t\t\t\tdude.vy = 0;\r\n\t\t\t\t\tdude.jumping = false;\r\n\r\n\t\t\t\t\tdude.vx -= dude.vx * friction;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (let index = platforms.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet platform = platforms[index];\r\n\r\n\t\t\t\t\tif (dude.x + tile_size * 0.5 > platform.left && dude.x + tile_size * 0.5 < platform.right) {\r\n\r\n\t\t\t\t\t\tif (dude.y + tile_size > platform.floor && dude.oy + tile_size <= platform.oldFloor) {\r\n\r\n\t\t\t\t\t\t\tairborne = false;\r\n\t\t\t\t\t\t\tdude.y = platform.floor - tile_size;\r\n\t\t\t\t\t\t\tdude.vy = platform.vy;\r\n\t\t\t\t\t\t\tdude.jumping = false;\r\n\t\t\t\t\t\t\tdude.vx += (platform.vx - dude.vx) * friction;\r\n\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (dude.jumping || airborne) {\r\n\r\n\t\t\t\t\tdude.vx -= dude.vx * friction;\r\n\r\n\t\t\t\t}\r\n\r\n\r\n\t\t\t}\r\n\r\n\t\t\tconst Platform = function(x, y, big, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, big ? 160 : 208, big ? 46 : 30, behavior);\r\n\r\n\t\t\t\tthis.anchor_x = x;\r\n\t\t\t\tthis.anchor_y = y;\r\n\t\t\t\tthis.d = 0;\r\n\t\t\t\tthis.w = big ? 32 : 16;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tPlatform.prototype = {\r\n\r\n\t\t\t\tget floor() { return this.y + 4; },\r\n\r\n\t\t\t\tget oldFloor() { return this.oy + 4; },\r\n\r\n\t\t\t\tget left() { return this.x + 3; },\r\n\t\t\t\tget right() { return this.x + 9 + this.w; }\r\n\r\n\t\t\t};\r\n\r\n\t\t\tObject.assign(Platform.prototype, Player.prototype);\r\n\r\n\t\t\tfunction platformBehaviorX(platform) {\r\n\r\n\t\t\t\tplatform.d += 0.01;\r\n\t\t\t\tplatform.ox = platform.x;\r\n\t\t\t\tplatform.vx = platform.anchor_x + Math.cos(platform.d) * 40 - platform.x;\r\n\t\t\t\tplatform.x += platform.vx;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction platformBehaviorY(platform) {\r\n\r\n\t\t\t\tplatform.d += 0.01;\r\n\t\t\t\tplatform.oy = platform.y;\r\n\t\t\t\tplatform.vy = platform.anchor_y + Math.sin(platform.d) * 40 - platform.y;\r\n\t\t\t\tplatform.y += platform.vy;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction platformBehaviorXY(platform) {\r\n\r\n\t\t\t\tplatform.d += 0.01\r\n\t\t\t\tplatform.ox = platform.x;\r\n\t\t\t\tplatform.vx = platform.anchor_x + Math.cos(platform.d) * 40 - platform.x;\r\n\t\t\t\tplatform.x += platform.vx;\r\n\t\t\t\tplatform.oy = platform.y;\r\n\t\t\t\tplatform.vy = platform.anchor_y + Math.sin(platform.d) * 40 - platform.y;\r\n\t\t\t\tplatform.y += platform.vy;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tvar tile_set = new Image();\r\n\t\t\tvar tile_size = 16;\r\n\t\t\tvar map_columns = 16;\r\n\t\t\tvar map_rows = 12;\r\n\t\t\tvar map_ratio = map_columns / map_rows;\r\n\t\t\tvar map_scale = 1;\r\n\t\t\tvar map = [1,1,0,0,1,0,0,1,1,0,1,0,1,1,0,0,\r\n\t\t\t\t\t\t\t   0,1,0,1,0,1,0,0,1,1,0,1,0,0,1,1,\r\n\t\t\t\t\t\t\t   1,0,1,1,1,0,0,1,0,0,0,1,0,1,0,0,\r\n\t\t\t\t\t\t\t   0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,\r\n\t\t\t\t\t\t\t   1,0,1,0,0,1,1,1,0,0,0,0,1,1,0,0,\r\n\t\t\t\t\t\t\t   0,1,0,0,1,0,0,0,0,1,0,1,0,0,1,0,\r\n\t\t\t\t\t\t\t   0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,\r\n\t\t\t\t\t\t\t   9,9,9,0,9,0,0,0,0,0,0,0,9,0,9,9,\r\n\t\t\t\t\t\t\t   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,\r\n\t\t\t\t\t\t\t   5,3,4,5,0,7,5,0,1,0,7,5,5,5,1,7,\r\n\t\t\t\t\t\t\t   5,6,6,5,1,7,5,3,3,3,4,5,8,5,3,4,\r\n\t\t\t\t\t\t\t   5,1,7,5,0,8,5,6,6,6,6,5,0,7,6,6];\r\n\r\n\t\t\tvar floor = 116;\r\n\t\t\tvar friction = 0.3;\r\n\t\t\tvar gravity = 1;\r\n\r\n\t\t\tvar context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\t\t\tvar buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n\r\n\t\t\tvar screen_h = document.documentElement.clientHeight - 16;\r\n\t\t\tvar screen_w = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\tvar controller = new Controller();\r\n\r\n\t\t\tvar platforms = [ new Platform(8, 64, false, platformBehaviorY), new Platform(96, 96, true, platformBehaviorX), new Platform(160, 64, false, platformBehaviorXY)];\r\n\r\n\t\t\tvar dude = new Dude(100, 100, dudeBehavior);\r\n\r\n\t\t\tfunction loop(time_step) {\r\n\r\n\t\t\t\twindow.requestAnimationFrame(loop);\r\n\r\n\t\t\t\tscreen_h = document.documentElement.clientHeight - 16;\r\n\t\t\t\tscreen_w = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\t\tif (screen_h / buffer.canvas.height < screen_w / buffer.canvas.width) screen_w = screen_h * map_ratio;\r\n\t\t\t\telse screen_h = screen_w / map_ratio;\r\n\r\n\t\t\t\tmap_scale = screen_h / (map_rows * tile_size);\r\n\r\n\t\t\t\tcontext.canvas.height = screen_h;\r\n\t\t\t\tcontext.canvas.width = screen_w;\r\n\t\t\t\tcontext.imageSmoothingEnabled = false;\r\n\r\n\t\t\t\tfor (let index = map.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet value = map[index];\r\n\t\t\t\t\tlet tile_x = (index % map_columns) * tile_size;\r\n\t\t\t\t\tlet tile_y = Math.floor(index / map_columns) * tile_size;\r\n\r\n\t\t\t\t\tbuffer.drawImage(tile_set, value * tile_size, 0, tile_size, tile_size, tile_x, tile_y, tile_size, tile_size);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (let index = platforms.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet platform = platforms[index];\r\n\r\n\t\t\t\t\tplatform.behave();\r\n\r\n\t\t\t\t\tbuffer.drawImage(tile_set, platform.source_x, 0, platform.source_w, tile_size, Math.round(platform.x), Math.round(platform.y), platform.source_w, tile_size);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tdude.behave();\r\n\r\n\t\t\t\tbuffer.drawImage(tile_set, dude.source_x, 0, tile_size, tile_size, Math.round(dude.x), Math.round(dude.y), tile_size, tile_size);\r\n\r\n\t\t\t\tcontext.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n\t\t\t}\r\n\r\n\t\t\tbuffer.canvas.height = map_rows * tile_size;\r\n\t\t\tbuffer.canvas.width = map_columns * tile_size;\r\n\r\n\t\t\ttile_set.addEventListener(\"load\", (event) => { loop(); });\r\n\t\t\ttile_set.src = \"platform.png\";\r\n\r\n\t\t\twindow.addEventListener(\"keydown\", (event) => { controller.keyDownUp(event); });\r\n\t\t\twindow.addEventListener(\"keyup\", (event) => { controller.keyDownUp(event); });\r\n\r\n\t\t</script>\r\n\r\n\t</body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/platformer-ai/platformer-ai.html",
    "content": "<!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\t\t\thtml, body { height:100%; width:100%; }\r\n\r\n\t\t\tbody { background-color:#000000; display:grid; }\r\n\r\n\t\t\tcanvas { align-self:center; justify-self:center; }\r\n\r\n\t\t</style>\r\n\r\n\t</head>\r\n\r\n\t<body>\r\n\r\n\t\t<canvas></canvas>\r\n\r\n\t\t<script type = \"text/javascript\">\r\n\r\n\t\t\tconst Player = function(x, y, source_x, behavior) {\r\n\r\n\t\t\t\tthis.x = x; this.y = y; this.source_x = source_x;\r\n\t\t\t\tthis.behavior = behavior;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tPlayer.prototype.behave = function() { this.behavior(this); }\r\n\r\n\t\t\tconst Block = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 160, behavior);\r\n\r\n\t\t\t\tthis.state = \"waiting\";\r\n\t\t\t\tthis.vy = 0;\r\n\t\t\t\tthis.anchor_y = y;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tObject.assign(Block.prototype, Player.prototype);\r\n\r\n\t\t\tconst Dude = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 144, behavior);\r\n\r\n\t\t\t\tthis.vx = this.vy = 0;\r\n\t\t\t\tthis.jumping = true;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tObject.assign(Dude.prototype, Player.prototype);\r\n\r\n\t\t\tconst Fox = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 192, behavior);\r\n\r\n\t\t\t\tthis.vx = this.vy = 0;\r\n\t\t\t\tthis.jumping = true;\r\n\t\t\t\tthis.target = undefined;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tObject.assign(Fox.prototype, Player.prototype);\r\n\r\n\t\t\tconst Ghost = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 128, behavior);\r\n\r\n\t\t\t\tthis.vx = 0;\r\n\t\t\t\tthis.destination_x = x;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tObject.assign(Ghost.prototype, Player.prototype);\r\n\r\n\t\t\tconst Spike = function(x, y, behavior) {\r\n\r\n\t\t\t\tPlayer.call(this, x, y, 176, behavior);\r\n\r\n\t\t\t\tthis.anchor_x = x;\r\n\t\t\t\tthis.anchor_y = y;\r\n\t\t\t\tthis.d = 0;\r\n\t\t\t\tthis.range = 20;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tObject.assign(Spike.prototype, Player.prototype);\r\n\r\n\t\t\tfunction blockBehavior(block) {\r\n\r\n\t\t\t\tswitch(block.state) {\r\n\r\n\t\t\t\t\tcase \"waiting\":\r\n\r\n\t\t\t\t\t\tfor (let index = players.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\t\t\tlet player = players[index];\r\n\r\n\t\t\t\t\t\t\tif (player == block) continue;\r\n\r\n\t\t\t\t\t\t\tif (player.x + tile_size * 0.5 > block.x && player.x + tile_size * 0.5 < block.x + tile_size) { block.state = \"falling\"; return; }\r\n\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"falling\":\r\n\r\n\t\t\t\t\t\tblock.vy += gravity;\r\n\r\n\t\t\t\t\t\tblock.y += block.vy;\r\n\r\n\t\t\t\t\t\tblock.vy *= friction;\r\n\r\n\t\t\t\t\t\tif (block.y > floor) { block.y = floor; block.vy = 0; this.state = \"rising\"; }\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \"rising\":\r\n\r\n\t\t\t\t\t\tblock.vy = -0.5;\r\n\r\n\t\t\t\t\t\tblock.y += block.vy;\r\n\r\n\t\t\t\t\t\tif (block.y < block.anchor_y) { block.y = block.anchor_y; block.vy = 0; block.state = \"waiting\"; }\r\n\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction dudeBehavior(dude) {\r\n\r\n\t\t\t\tdude.vy += gravity;\r\n\r\n\t\t\t\tif (pointer.down && !dude.jumping && pointer.y < dude.y) {\r\n\r\n\t\t\t\t\tdude.jumping = true;\r\n\t\t\t\t\tdude.vy = -16;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (dude.jumping) dude.vx = (pointer.x - dude.x - tile_size * 0.5) * 0.1;\r\n\t\t\t\telse dude.vx = (pointer.x - dude.x - tile_size * 0.5) * 0.025;\r\n\r\n\t\t\t\tdude.x += dude.vx;\r\n\t\t\t\tdude.y += dude.vy;\r\n\r\n\t\t\t\tdude.vy *= friction;\r\n\r\n\t\t\t\tif (dude.y > floor) { dude.y = floor; dude.jumping = false; }\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction foxBehavior(fox) {\r\n\r\n\t\t\t\tlet d = fox.target.x - fox.x;\r\n\r\n\t\t\t\tfox.vy += gravity;\r\n\r\n\t\t\t\tif (d * d > 1000) {\r\n\r\n\t\t\t\t\tif (fox.jumping)fox.vx += d * 0.025;\r\n\t\t\t\t\telse fox.vx = d * 0.025;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (fox.target.jumping && !fox.jumping) {\r\n\r\n\t\t\t\t\tfox.jumping = true;\r\n\t\t\t\t\tfox.vy = -10;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (fox.vx > 4) fox.vx = 4;\r\n\t\t\t\telse if (fox.vx < -4) fox.vx = -4;\r\n\r\n\t\t\t\tfox.x += fox.vx;\r\n\t\t\t\tfox.y += fox.vy;\r\n\r\n\t\t\t\tfox.vx *= friction;\r\n\t\t\t\tfox.vy *= friction;\r\n\r\n\t\t\t\tif (fox.y > floor) { fox.jumping = false; fox.y = floor; fox.vy = 0; }\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction ghostBehavior(ghost) {\r\n\r\n\t\t\t\tlet d = ghost.destination_x - ghost.x;\r\n\r\n\t\t\t\tghost.vx += d * 0.001;\r\n\r\n\t\t\t\tif (d * d < 16) ghost.destination_x = Math.random() * map_columns * tile_size - tile_size;\r\n\r\n\t\t\t\tif (ghost.vx > 0.5) ghost.vx = 0.5;\r\n\t\t\t\telse if (ghost.vx < -0.5) ghost.vx = -0.5;\r\n\r\n\t\t\t\tghost.x += ghost.vx;\r\n\r\n\t\t\t\tghost.vx *= friction;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction spikeBehaviorA(spike) {\r\n\r\n\t\t\t\tspike.d += 0.05;\r\n\r\n\t\t\t\tspike.x = spike.anchor_x + Math.cos(spike.d) * spike.range;\r\n\t\t\t\tspike.y = spike.anchor_y + Math.sin(spike.d) * spike.range * 0.5;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tfunction spikeBehaviorB(spike) {\r\n\r\n\t\t\t\tspike.d += 0.05;\r\n\r\n\t\t\t\tspike.x = spike.anchor_x + Math.cos(spike.d) * spike.range * 0.5;\r\n\t\t\t\tspike.y = spike.anchor_y + Math.sin(spike.d) * spike.range;\r\n\r\n\t\t\t}\r\n\r\n\t\t\tvar tile_set = new Image();\r\n\t\t\tvar tile_size = 16;\r\n\t\t\tvar map_columns = 16;\r\n\t\t\tvar map_rows = 12;\r\n\t\t\tvar map_ratio = map_columns / map_rows;\r\n\t\t\tvar map_scale = 1;\r\n\t\t\tvar map = [1,1,0,0,1,0,0,1,1,0,1,0,1,1,0,0,\r\n\t\t\t\t\t\t\t   0,1,0,1,0,1,0,0,1,1,0,1,0,0,1,1,\r\n\t\t\t\t\t\t\t   1,0,1,1,1,0,0,1,0,0,0,1,0,1,0,0,\r\n\t\t\t\t\t\t\t   0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,\r\n\t\t\t\t\t\t\t   1,0,1,0,0,1,1,1,0,0,0,0,1,1,0,0,\r\n\t\t\t\t\t\t\t   0,1,0,0,1,0,0,0,0,1,0,1,0,0,1,0,\r\n\t\t\t\t\t\t\t   0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,\r\n\t\t\t\t\t\t\t   7,7,0,7,0,0,0,0,0,7,0,7,0,0,7,0,\r\n\t\t\t\t\t\t\t   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\r\n\t\t\t\t\t\t\t   4,4,4,4,4,4,4,4,4,4,5,0,2,4,4,5,\r\n\t\t\t\t\t\t\t   4,4,4,4,4,5,2,4,4,4,0,0,3,4,5,0,\r\n\t\t\t\t\t\t\t   5,0,2,4,5,0,3,4,4,5,0,0,3,4,0,0];\r\n\r\n\t\t\tvar floor = 116;\r\n\t\t\tvar friction = 0.9;\r\n\t\t\tvar gravity = 1;\r\n\r\n\t\t\tvar context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\t\t\tvar buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n\r\n\t\t\tvar screen_h = document.documentElement.clientHeight - 16;\r\n\t\t\tvar screen_w = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\tvar pointer = { x:map_columns * tile_size * 0.5, y:0, down:false };\r\n\r\n\t\t\tvar players = [ new Block(32, 16, blockBehavior),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Dude(64, floor, dudeBehavior),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Fox (80, floor, foxBehavior),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Ghost(96, floor, ghostBehavior),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Ghost(112, floor, ghostBehavior),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Spike(160, 80, spikeBehaviorA),\r\n\t\t\t\t\t\t\t\t\t\t\tnew Spike(208, 80, spikeBehaviorB)];\r\n\r\n\t\t\tplayers[2].target = players[1];\r\n\r\n\t\t\tfunction loop() {\r\n\r\n\t\t\t\twindow.requestAnimationFrame(loop);\r\n\r\n\t\t\t\tscreen_h = document.documentElement.clientHeight - 16;\r\n\t\t\t\tscreen_w = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\t\tif (screen_h / buffer.canvas.height < screen_w / buffer.canvas.width) screen_w = screen_h * map_ratio;\r\n\t\t\t\telse screen_h = screen_w / map_ratio;\r\n\r\n\t\t\t\tmap_scale = screen_h / (map_rows * tile_size);\r\n\r\n\t\t\t\tcontext.canvas.height = screen_h;\r\n\t\t\t\tcontext.canvas.width = screen_w;\r\n\t\t\t\tcontext.imageSmoothingEnabled = false;\r\n\r\n\t\t\t\tfor (let index = map.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet value = map[index];\r\n\t\t\t\t\tlet tile_x = (index % map_columns) * tile_size;\r\n\t\t\t\t\tlet tile_y = Math.floor(index / map_columns) * tile_size;\r\n\r\n\t\t\t\t\tbuffer.drawImage(tile_set, value * tile_size, 0, tile_size, tile_size, tile_x, tile_y, tile_size, tile_size);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (let index = players.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet player = players[index];\r\n\r\n\t\t\t\t\tplayer.behave();\r\n\r\n\t\t\t\t\t// Added Math.round after video\r\n\t\t\t\t\tbuffer.drawImage(tile_set, player.source_x, 0, tile_size, tile_size, Math.round(player.x), Math.round(player.y), tile_size, tile_size);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tpointer.down = false;\r\n\r\n\t\t\t\tcontext.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n\t\t\t}\r\n\r\n\t\t\tbuffer.canvas.height = map_rows * tile_size;\r\n\t\t\tbuffer.canvas.width = map_columns * tile_size;\r\n\r\n\t\t\ttile_set.addEventListener(\"load\", (event) => { loop(); });\r\n\t\t\ttile_set.src = \"platformer-ai.png\";\r\n\r\n\t\t\tcontext.canvas.addEventListener(\"click\", (event) => {\r\n\r\n\t\t\t\tvar rectangle = event.target.getBoundingClientRect();\r\n\r\n\t\t\t\tpointer.x = (event.pageX - rectangle.left) / map_scale;\r\n\t\t\t\tpointer.y = (event.pageY - rectangle.top) / map_scale;\r\n\t\t\t\tpointer.down = true;\r\n\r\n\t\t\t});\r\n\r\n\t\t</script>\r\n\r\n\t</body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/polygon/polygon.html",
    "content": "<!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 example.\">\r\n    <meta name = \"viewport\" content = \"width=device-width, user-scalable=no\">\r\n\r\n    <title>JS Polygon Example</title>\r\n\r\n    <style>\r\n\r\n      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }\r\n\r\n      body, html { height:100%; width:100%; }\r\n\r\n      body { background-color:#202830; display:grid; }\r\n\r\n      canvas { align-self:center; display:grid; justify-self:center; }\r\n    \r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n    \r\n        /////////////////\r\n       //// CLASSES ////\r\n      /////////////////\r\n\r\n      class Point2D {\r\n\r\n        constructor(x, y) {\r\n\r\n          this.x = x; this.y = y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Polygon2D {\r\n\r\n        constructor(x, y, ...vertices) {\r\n          \r\n          this.position = new Point2D(x, y);\r\n          this.rotation = 0;\r\n          this.scale = 1;\r\n          this.vertices = new Array(); // points in the polygon\r\n\r\n          for (let index = vertices.length - 2; index > -1; index -= 2) {\r\n\r\n            this.vertices[index * 0.5] = new Point2D(vertices[index], vertices[index + 1]);\r\n\r\n          }\r\n        \r\n        }\r\n\r\n        /* Move the polygon by its reference point to the specified location. This entails getting the vector between the reference point and the specified point and then moving each point in the polygon by that vector and finally setting the reference point or position to equal the specified point. */\r\n        setPosition(x, y) {\r\n\r\n          var vector_x = x - this.position.x;\r\n          var vector_y = y - this.position.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n            \r\n            vertex.x += vector_x;\r\n            vertex.y += vector_y;\r\n\r\n          }\r\n\r\n          this.position.x += vector_x;\r\n          this.position.y += vector_y;\r\n\r\n        }\r\n\r\n        /* Rotate the polygon to the specified value. First we get the difference between the current rotation and the desired rotation, then we get the unit sized cartesian coordinates of our rotation vector, then we loop through each vertex, we move each vertex back to the origin, we rotate by the unit vector multiplied by the length of the vector from the origin to the vertex and then we translate back to the original position. Finally we set the current rotation equal to the desired location. */\r\n        setRotation(rotation) {\r\n\r\n          var radians = rotation - this.rotation;\r\n          var unit_x = Math.cos(radians); // Check out a unit circle to better understand this.\r\n          var unit_y = Math.sin(radians);\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n            \r\n            let vector_x = vertex.x - this.position.x; // Translate the vertex to the origin.\r\n            let vector_y = vertex.y - this.position.y;\r\n\r\n            vertex.x = vector_x * unit_x - vector_y * unit_y + this.position.x;\r\n            vertex.y = vector_x * unit_y + vector_y * unit_x + this.position.y; // \r\n\r\n          }\r\n\r\n          this.rotation = rotation;\r\n\r\n        }\r\n\r\n        /* Scale the polygon to the specified size. First get the ratio between the desired scalar value and the current scale of the polygon, then get the vector between the reference point and each point in the polygon. Then set each point in the polygon equal to the reference point plus the vector multiplied by the ratio between scalar values. Finally, set the current scale to the specified scale. */\r\n        setScale(scale) {\r\n\r\n          var ratio = scale / this.scale;\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n\r\n            let vector_x = vertex.x - this.position.x;\r\n            let vector_y = vertex.y - this.position.y;\r\n\r\n            vertex.x = vector_x * ratio + this.position.x;\r\n            vertex.y = vector_y * ratio + this.position.y;\r\n\r\n          }\r\n\r\n          this.scale = scale;\r\n\r\n        }\r\n\r\n      }\r\n\r\n        ///////////////////\r\n       //// VARIABLES ////\r\n      ///////////////////\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\", { alpha:false });\r\n\r\n      var polygon = new Polygon2D(0, 0, -1, -1, 0, -2, 1, -1, 1, 1, 0, 2, -1, 1);\r\n\r\n      polygon.setScale(40); // 40 fits on my phone nicely\r\n\r\n        ///////////////////\r\n       //// FUNCTIONS ////\r\n      ///////////////////\r\n\r\n      function drawPolygon2D(polygon2d, color) {\r\n\r\n        var vertex = polygon2d.vertices[0];\r\n\r\n        context.beginPath();\r\n        context.moveTo(vertex.x, vertex.y);\r\n\r\n        for (let index = polygon2d.vertices.length - 1; index > 0; -- index) {\r\n\r\n          vertex = polygon2d.vertices[index];\r\n          context.lineTo(vertex.x, vertex.y);\r\n\r\n        }\r\n\r\n        context.fillStyle = color;\r\n        context.fill();\r\n\r\n      }\r\n\r\n      function loop(time_stamp) {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        context.fillStyle = \"#c0f0d8\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        polygon.setRotation(polygon.rotation - 0.01);\r\n\r\n        drawPolygon2D(polygon, \"#0080f0\");\r\n\r\n      }\r\n\r\n        /////////////////////////\r\n       //// EVENT LISTENERS ////\r\n      /////////////////////////\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n        context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n        polygon.setPosition(context.canvas.width * 0.5, context.canvas.height * 0.5);\r\n\r\n        context.imageSmoothingEnabled = false;\r\n\r\n      }\r\n\r\n        ////////////////////\r\n       //// INITIALIZE ////\r\n      ////////////////////\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      resize();\r\n      loop();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/polygon-rotation/polygon-rotation.html",
    "content": "<!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 = \"description\" content = \"An html5 canvas application that showcases rotating polygons\">\r\n    <title>Polygon Rotation</title>\r\n    <style>\r\n      * { margin:0; overflow:hidden; padding:0; pointer-events:none; user-select:none; }\r\n      html, body{ height:100%; width:100%; }\r\n      body { background-color:#000000; display:grid; }\r\n      #canvas { align-self:center; justify-self:center; }\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas id = \"canvas\"></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Polygon {\r\n\r\n        constructor(x, y, vertices, color) {\r\n\r\n          this.x = x; this.y = y; this.r = 0; this.color = color;\r\n          this.vertices = [...vertices];\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; index -= 2) {\r\n\r\n            this.vertices[index - 1] += this.x;\r\n            this.vertices[index]     += this.y;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          var vector_x = x - this.x;\r\n          var vector_y = y - this.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; index -= 2) {\r\n\r\n            this.vertices[index - 1] = this.vertices[index - 1] + vector_x;\r\n            this.vertices[index]     = this.vertices[index]     + vector_y;\r\n\r\n          }\r\n\r\n          this.x = x;\r\n          this.y = y;\r\n\r\n        }\r\n\r\n        rotateTo(radians) {\r\n\r\n          let rotation = radians - this.r;\r\n          this.r += rotation;\r\n\r\n          var vector_x = Math.cos(rotation);\r\n          var vector_y = Math.sin(rotation);\r\n\r\n          for (let index = this.vertices.length - 1; index > 0; index -= 2) {\r\n\r\n            let x = this.vertices[index - 1] - this.x;\r\n            let y = this.vertices[index]     - this.y;\r\n\r\n            this.vertices[index - 1] = x * vector_x - y * vector_y + this.x;\r\n            this.vertices[index]     = x * vector_y + y * vector_x + this.y;\r\n\r\n          }\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var context = document.getElementById(\"canvas\").getContext(\"2d\", { alpha:false });\r\n\r\n      var arrow_vertices = [-10, -5, 0, -5, 0, -10, 15, 0, 0, 10, 0, 5, -10, 5];\r\n      \r\n      var polygons = new Array();\r\n\r\n      var pointer = { down:false, x:0, y:0 };\r\n\r\n      function drawPolygon(polygon, context) {\r\n\r\n        context.beginPath();\r\n        context.moveTo(polygon.vertices[0], polygon.vertices[1]);\r\n        for (let index = polygon.vertices.length - 1; index > 2; index -= 2) {\r\n\r\n          context.lineTo(polygon.vertices[index - 1], polygon.vertices[index]);\r\n\r\n        }\r\n        context.closePath();\r\n        context.fillStyle = polygon.color;\r\n        context.fill();\r\n\r\n      }\r\n\r\n      function generatePolygons(cell_size) {\r\n\r\n        var columns = Math.floor(context.canvas.width / cell_size);\r\n        var rows    = Math.floor(context.canvas.height / cell_size);\r\n\r\n        var cell_w = context.canvas.width / columns;\r\n        var cell_h = context.canvas.height / rows;\r\n\r\n        var color_x = Math.floor(Math.random() * columns) * cell_w;\r\n        var color_y = Math.floor(Math.random() * rows) * cell_h;\r\n        var color_randomizer = Math.random();\r\n        var color_seed = Math.random() * Math.PI * 2;\r\n\r\n        polygons = new Array();\r\n\r\n        for (let index = columns * rows - 1; index > -1; -- index) {\r\n\r\n          let x = (index % columns) * cell_w + cell_w * 0.5;\r\n          let y = Math.floor(index / columns) * cell_h + cell_h * 0.5;\r\n \r\n          polygons[index] = new Polygon(0, 0, arrow_vertices, makeAColor(Math.sqrt((color_x - x) * (color_x - x) + (color_y - y) * (color_y - y)) * 0.01 + color_seed, color_randomizer));\r\n\r\n          polygons[index].moveTo(x, y);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function loop(time_stamp) {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n        \r\n        for (let index = polygons.length - 1; index > -1; -- index) {\r\n\r\n          let polygon = polygons[index];\r\n\r\n          if (pointer.down) {\r\n           \r\n            let r = Math.atan2(pointer.y - polygon.y, pointer.x - polygon.x);polygon.rotateTo(r);\r\n\r\n          }\r\n\r\n          drawPolygon(polygon, context);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function makeAColor(radian, randomizer) {\r\n\r\n        return \"rgba(\" + Math.floor(Math.sin(radian * (1 - randomizer)) * 256) + \", \" + Math.floor(Math.sin(radian * randomizer) * 256) + \",\" + Math.floor(Math.sin(radian * randomizer) * 256) + \",0.625)\";\r\n\r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight - 16;\r\n        context.canvas.width  = document.documentElement.clientWidth  - 16;\r\n\r\n        generatePolygons(32);\r\n\r\n      }\r\n\r\n      function mouseDownMoveUp(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - rectangle.left;\r\n        pointer.y = event.clientY - rectangle.top;\r\n\r\n        if (event.type == \"mousedown\") pointer.down    = true;\r\n        else if (event.type == \"mouseup\") pointer.down = false;\r\n\r\n      }\r\n\r\n      function touchEndMoveStart(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n        if (event.type != \"touchend\") {\r\n          pointer.down = true;\r\n          pointer.x = event.touches[0].clientX - rectangle.left;\r\n          pointer.y = event.touches[0].clientY - rectangle.top;\r\n        } else pointer.down = false;\r\n\r\n      }\r\n\r\n      window.addEventListener(\"resize\", resize);\r\n\r\n      window.addEventListener(\"mousedown\", mouseDownMoveUp, { passive:false });\r\n      window.addEventListener(\"mousemove\", mouseDownMoveUp, { passive:false });\r\n      window.addEventListener(\"mouseup\",   mouseDownMoveUp, { passive:false });\r\n\r\n      window.addEventListener(\"touchend\", touchEndMoveStart, { passive:false });\r\n      window.addEventListener(\"touchmove\", touchEndMoveStart, { passive:false });\r\n      window.addEventListener(\"touchstart\", touchEndMoveStart, { passive:false });\r\n\r\n      resize();\r\n      loop();\r\n    \r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.css",
    "content": "/* 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%; width:100%; }\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nh1 {\r\n\r\n  font-size:3.0em;\r\n  text-align:center;\r\n\r\n}\r\n\r\ncanvas { user-select:none; }\r\n\r\np { padding:0 8px; max-width:480px; text-align:justify; }\r\n\r\n#ui {\r\n\r\n  display:grid;\r\n  grid-template-columns:auto auto auto;\r\n  grid-template-rows:auto;\r\n\r\n}\r\n\r\n#ui a {\r\n\r\n  align-content:center;\r\n  border-color:#ffffff;\r\n  border-radius:8px;\r\n  border-style:solid;\r\n  border-width:1px;\r\n  cursor:pointer;\r\n  display:grid;\r\n  font-size:1.25em;\r\n  font-weight:800;\r\n  justify-content:center;\r\n  margin:0 8px;\r\n  padding:8px;\r\n  text-align:center;\r\n  user-select:none;\r\n\r\n}\r\n"
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.html",
    "content": "<!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/css\">\r\n\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \"description\" content = \"Tests two methods for scaling graphics with HTML5 canvas. Find out which is faster!\">\r\n\r\n    <title>PoP Vlog - Pre-Scale Performance</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog<br>Pre-Scale Performance</h1>\r\n\r\n    <p></p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <div id = \"ui\">\r\n\r\n      <a>+ 100</a><a>method: scale</a><a>- 100</a>\r\n\r\n    </div>\r\n\r\n    <p>This application compares the rendering speed of three drawing methods: scale,\r\n      pre-scale, and pre-scaled-sheet. Scale draws graphics from the source image\r\n      to a buffer, which is then scaled and drawn to the screen. Pre-scale creates\r\n      individual scaled canvas elements for each sprite in the source image and then\r\n      draws them directly to the screen. Pre-scaled-sheet draws the source image to\r\n      a larger canvas element, and then those scaled graphics are drawn directly to\r\n      the screen.</p>\r\n\r\n    <script src = \"pre-scale-performance.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/pre-scale-performance/pre-scale-performance.js",
    "content": "// 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 = Math.floor(Math.random() * 4);\r\n    this.x = x;\r\n    this.x_velocity;\r\n    this.y = y;\r\n    this.y_velocity;\r\n\r\n  };\r\n\r\n  Ball.reset = function(ball) {\r\n\r\n    var direction = Math.random() * (Math.PI * 2);\r\n\r\n    ball.x = Math.random() * (game.world.width - display.tile_sheet.tile_size);\r\n    ball.x_velocity = Math.cos(direction) * 2;\r\n    ball.y = Math.random() * (game.world.height - display.tile_sheet.tile_size);\r\n    ball.y_velocity = Math.sin(direction) * 2;\r\n\r\n  };\r\n\r\n  Ball.prototype = {\r\n\r\n    constructor:Ball,\r\n\r\n    update:function() {\r\n\r\n      this.x += this.x_velocity;\r\n      this.y += this.y_velocity;\r\n\r\n    },\r\n\r\n    collideWorld:function() {\r\n\r\n      if (this.x < 0) {\r\n\r\n        this.x = 0;\r\n        this.x_velocity *= -1;\r\n\r\n      } else if (this.x + display.tile_sheet.tile_size > game.world.width) {\r\n\r\n        this.x = game.world.width - display.tile_sheet.tile_size;\r\n        this.x_velocity *= -1;\r\n\r\n      }\r\n\r\n      if (this.y < 0) {\r\n\r\n        this.y = 0;\r\n        this.y_velocity *= -1;\r\n\r\n      } else if (this.y + display.tile_sheet.tile_size > game.world.height) {\r\n\r\n        this.y = game.world.height - display.tile_sheet.tile_size;\r\n        this.y_velocity *= -1;\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  const Pool = function(constructor_name) {\r\n\r\n    this.constructor_name = constructor_name;\r\n    this.active_objects = new Array();\r\n    this.stored_objects = new Array();\r\n\r\n  };\r\n\r\n  Pool.prototype = {\r\n\r\n    constructor:Pool,\r\n\r\n    activate:function(number, callback) {\r\n\r\n      var object;\r\n\r\n      for (let index = 0; index < number; index ++) {\r\n\r\n        if (this.stored_objects.length != 0) {\r\n\r\n          object = this.stored_objects.pop();\r\n\r\n        } else {\r\n\r\n          object = new this.constructor_name();\r\n\r\n        }\r\n\r\n        if (callback) { callback(object) }\r\n\r\n        this.active_objects.push(object);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    store:function(number) {\r\n\r\n      while(this.active_objects.length != 0 && number > 0) {\r\n\r\n        number --;\r\n\r\n        this.stored_objects.push(this.active_objects.pop());\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  var display, game, renderPreScale, renderScale, renderScaledSheet, tracker;\r\n\r\n  renderScaledSheet = function() {\r\n\r\n    //display.context.fillStyle = \"#ffffff\";\r\n    //display.context.fillRect(0, 0, display.context.canvas.width, display.context.canvas.height);\r\n\r\n    var scaled_size = display.tile_sheet.tile_size * display.scale;\r\n\r\n    for (let index = game.object_manager.ball_pool.active_objects.length - 1; index > -1; -- index) {\r\n\r\n      let ball = game.object_manager.ball_pool.active_objects[index];\r\n\r\n      display.context.drawImage(display.tile_sheet.scaled_image.canvas,\r\n                               (ball.frame_index % display.tile_sheet.columns) * scaled_size,\r\n                               Math.floor(ball.frame_index / display.tile_sheet.columns) * scaled_size,\r\n                               scaled_size, scaled_size,\r\n                               ball.x * display.scale, ball.y * display.scale, scaled_size, scaled_size);\r\n    }\r\n\r\n  };\r\n\r\n  renderPreScale = function() {\r\n\r\n    //display.context.fillStyle = \"#ffffff\";\r\n    //display.context.fillRect(0, 0, display.context.canvas.width, display.context.canvas.height);\r\n\r\n    for (let index = game.object_manager.ball_pool.active_objects.length - 1; index > -1; -- index) {\r\n\r\n      let ball = game.object_manager.ball_pool.active_objects[index];\r\n      let graphic = display.tile_sheet.prerendered_frames[ball.frame_index];\r\n\r\n      display.context.drawImage(graphic, 0, 0, graphic.width, graphic.height, ball.x * display.scale, ball.y * display.scale, graphic.width, graphic.height);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  renderScale = function() {\r\n\r\n    //display.buffer.fillStyle = \"#ffffff\";\r\n    //display.buffer.fillRect(0, 0, display.buffer.canvas.width, display.buffer.canvas.height);\r\n\r\n    for (let index = game.object_manager.ball_pool.active_objects.length - 1; index > -1; -- index) {\r\n\r\n      let ball = game.object_manager.ball_pool.active_objects[index];\r\n      let graphic = display.tile_sheet.frames[ball.frame_index];\r\n\r\n      display.buffer.drawImage(graphic,\r\n                               0, 0, graphic.width, graphic.height,\r\n                               ball.x, ball.y, graphic.width, graphic.height);\r\n\r\n    }\r\n\r\n    display.context.drawImage(display.buffer.canvas, 0, 0, display.buffer.canvas.width, display.buffer.canvas.height, 0, 0, display.context.canvas.width, display.context.canvas.height);\r\n\r\n  };\r\n\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    p:document.querySelector(\"p\"),\r\n\r\n    tile_sheet: {\r\n\r\n      columns:2,\r\n      rows:2,\r\n      image:new Image(),\r\n      scaled_image:document.createElement(\"canvas\").getContext(\"2d\"),\r\n      prerendered_frames:[],\r\n      frames:[],\r\n      tile_size:16,\r\n\r\n      prerenderFrames:function(scale) {\r\n\r\n        var graphic, scaled_graphic;\r\n\r\n        for (let index = this.columns * this.rows; index > -1; -- index) {\r\n\r\n          graphic = document.createElement(\"canvas\").getContext(\"2d\");\r\n          graphic.canvas.height = graphic.canvas.width = this.tile_size;\r\n          graphic.imageSmoothingEnabled = false;\r\n\r\n          graphic.drawImage(this.image,\r\n                            (index % this.columns) * this.tile_size,\r\n                            Math.floor(index / this.columns) * this.tile_size,\r\n                            this.tile_size, this.tile_size,\r\n                            0, 0, graphic.canvas.width, graphic.canvas.height);\r\n\r\n          this.frames[index] = graphic.canvas;\r\n\r\n          scaled_graphic = document.createElement(\"canvas\").getContext(\"2d\");\r\n          scaled_graphic.canvas.height = scaled_graphic.canvas.width = this.tile_size * scale;\r\n          scaled_graphic.imageSmoothingEnabled = false;\r\n\r\n          scaled_graphic.drawImage(graphic.canvas,\r\n                            0, 0, graphic.canvas.width, graphic.canvas.height,\r\n                            0, 0, scaled_graphic.canvas.width, scaled_graphic.canvas.height);\r\n\r\n          this.prerendered_frames[index] = scaled_graphic.canvas;\r\n\r\n        }\r\n\r\n        this.scaled_image.canvas.height = this.image.height * scale;\r\n        this.scaled_image.canvas.width = this.image.width * scale;\r\n        this.scaled_image.imageSmoothingEnabled = false;\r\n        this.scaled_image.drawImage(this.image, 0, 0, this.image.width, this.image.height,\r\n                                    0, 0, this.scaled_image.canvas.width, this.scaled_image.canvas.height);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    render:renderScale,\r\n\r\n    resize:function(event) {\r\n\r\n      var height, width;\r\n\r\n      height = document.documentElement.clientHeight;\r\n      width = document.documentElement.clientWidth;\r\n\r\n      display.context.canvas.width = Math.floor(width / display.tile_sheet.tile_size) * display.tile_sheet.tile_size;\r\n\r\n      if (display.context.canvas.width > height) {\r\n\r\n        display.context.canvas.width = Math.floor(height / display.tile_sheet.tile_size) * display.tile_sheet.tile_size;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = display.context.canvas.width * (game.world.height / game.world.width);\r\n\r\n      display.scale = display.context.canvas.width / game.world.width;\r\n\r\n      display.tile_sheet.prerenderFrames(display.scale);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  game = {\r\n\r\n    engine: {\r\n\r\n      accumulated_time:undefined,\r\n      animation_frame_request:undefined,\r\n      time:undefined,\r\n      time_step:1000/60,\r\n      needs_redraw:false,\r\n\r\n      loop:function(time_stamp) {\r\n\r\n        game.engine.animation_frame_request = window.requestAnimationFrame(game.engine.loop);\r\n\r\n        game.engine.accumulated_time += time_stamp - game.engine.time;\r\n        game.engine.time = time_stamp;\r\n\r\n        while (game.engine.accumulated_time >= game.engine.time_step) {\r\n\r\n          game.engine.accumulated_time -= game.engine.time_step;\r\n\r\n          game.engine.update();\r\n          game.engine.needs_redraw = true;\r\n\r\n        }\r\n\r\n        if (game.engine.needs_redraw) {\r\n\r\n          game.engine.render();\r\n\r\n        }\r\n\r\n      },\r\n\r\n      render:function() {\r\n\r\n        var time = window.performance.now();\r\n\r\n        display.render();\r\n\r\n        time = window.performance.now() - time;\r\n\r\n        tracker.iteration ++;\r\n        tracker.time += time;\r\n        tracker.average = tracker.time / tracker.iteration;\r\n\r\n        display.p.innerHTML = display.render.name + \": \" + tracker.average.toPrecision(2) + \" ms / frame to render\";\r\n\r\n      },\r\n\r\n      update:function() {\r\n\r\n        for (let index = game.object_manager.ball_pool.active_objects.length - 1; index > -1; -- index) {\r\n\r\n          let ball = game.object_manager.ball_pool.active_objects[index];\r\n\r\n          ball.update();\r\n          ball.collideWorld();\r\n\r\n        }\r\n\r\n      },\r\n\r\n      start:function() {\r\n\r\n        this.animation_frame_request = window.requestAnimationFrame(this.loop);\r\n        this.accumulated_time = this.time_step;\r\n        this.time = window.performance.now();\r\n\r\n      }\r\n\r\n    },\r\n\r\n    object_manager: {\r\n\r\n      ball_pool: new Pool(Ball)\r\n\r\n    },\r\n\r\n    world:{\r\n\r\n      height:360,\r\n      width:640\r\n\r\n    }\r\n\r\n  };\r\n\r\n  tracker = {\r\n\r\n    average:0,\r\n    iteration:0,\r\n    time:0,\r\n\r\n    reset:function() {\r\n\r\n      this.average = 0;\r\n      this.iteration = 0;\r\n      this.time = 0;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  //// INITIALIZE ////\r\n\r\n  let buttons = document.querySelectorAll(\"a\");\r\n\r\n  for (let index = buttons.length - 1; index > -1; -- index) {\r\n\r\n    buttons[index].addEventListener(\"click\", function(event) {\r\n\r\n      switch(this.innerHTML) {\r\n\r\n        case \"+ 100\":\r\n\r\n          game.object_manager.ball_pool.activate(100, Ball.reset);\r\n\r\n        break;\r\n        case \"- 100\":\r\n\r\n          game.object_manager.ball_pool.store(100);\r\n\r\n        break;\r\n        case \"method: scale\":\r\n\r\n          this.innerHTML = \"method: pre-scale\";\r\n          display.render = renderPreScale;\r\n\r\n        break;\r\n        case \"method: pre-scale\":\r\n\r\n          this.innerHTML = \"method: pre-scaled-sheet\";\r\n          display.render = renderScaledSheet;\r\n\r\n        break;\r\n\r\n        case \"method: pre-scaled-sheet\":\r\n\r\n          this.innerHTML = \"method: scale\";\r\n          display.render = renderScale;\r\n\r\n        break;\r\n\r\n      }\r\n\r\n      tracker.reset();\r\n\r\n    });\r\n\r\n  }\r\n\r\n  display.tile_sheet.image.addEventListener(\"load\", function(event) {\r\n\r\n    display.buffer.canvas.height = game.world.height;\r\n    display.buffer.canvas.width = game.world.width;\r\n\r\n    display.resize();\r\n\r\n    game.engine.start();\r\n\r\n  });\r\n\r\n  display.tile_sheet.image.src = \"pre-scale-performance.png\";\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/prototype-inheritance/prototype-inheritance.html",
    "content": "<!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>Inheritance</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <script src = \"prototype-inheritance.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/prototype-inheritance/prototype-inheritance.js",
    "content": "// 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:Human,\r\n\r\n  talk:function() {\r\n\r\n    console.log(\"Hey, I'm a human and my name is \" + this.name);\r\n\r\n  }\r\n\r\n};\r\n\r\nfunction Worker(name, job) {\r\n\r\n  Human.call(this, name);\r\n\r\n  this.job = job;\r\n\r\n}\r\n\r\nWorker.prototype = Object.create(Human.prototype);\r\nWorker.prototype.constructor = Worker;\r\nWorker.prototype.talk = function() {\r\n\r\n  console.log(\"Hey, my name is \" + this.name + \" and I am a \" + this.job + \". I am a \" + this.constructor.name);\r\n\r\n};\r\n\r\n\r\nvar human = new Human(\"Tim\");\r\nvar worker = new Worker(\"John\", \"desk jockey\");\r\n\r\nworker.talk();\r\n"
  },
  {
    "path": "content/rabbit-trap/01/controller-01.js",
    "content": "// 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 also defines the ButtonInput class, which is used for tracking button states. */\r\n\r\nconst Controller = function() {\r\n\r\n  this.down  = new Controller.ButtonInput();\r\n  this.left  = new Controller.ButtonInput();\r\n  this.right = new Controller.ButtonInput();\r\n  this.up    = new Controller.ButtonInput();\r\n\r\n  this.keyDownUp = function(event) {\r\n\r\n    var down = (event.type == \"keydown\") ? true : false;\r\n\r\n    switch(event.keyCode) {\r\n\r\n      case 37: this.left.getInput(down);  break;\r\n      case 38: this.up.getInput(down);    break;\r\n      case 39: this.right.getInput(down); break;\r\n      case 40: this.down.getInput(down);\r\n\r\n    }\r\n\r\n    alert(\"You pressed a key (\" + event.keyCode + \")!\");\r\n\r\n  };\r\n\r\n  this.handleKeyDownUp = (event) => { this.keyDownUp(event); };\r\n\r\n};\r\n\r\nController.prototype = {\r\n\r\n  constructor : Controller\r\n\r\n};\r\n\r\nController.ButtonInput = function() {\r\n\r\n  this.active = this.down = false;\r\n\r\n};\r\n\r\nController.ButtonInput.prototype = {\r\n\r\n  constructor : Controller.ButtonInput,\r\n\r\n  getInput : function(down) {\r\n\r\n    if (this.down != down) this.active = down;\r\n    this.down = down;\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/01/display-01.js",
    "content": "// Frank Poth 02/28/2018\r\n\r\n/* This Display class contains the screen resize event handler and also handles\r\ndrawing colors to the buffer and then to the display. */\r\n\r\nconst Display = function(canvas) {\r\n\r\n  this.buffer  = document.createElement(\"canvas\").getContext(\"2d\"),\r\n  this.context = canvas.getContext(\"2d\");\r\n\r\n  this.renderColor = function(color) {\r\n\r\n    this.buffer.fillStyle = color;\r\n    this.buffer.fillRect(0, 0, this.buffer.canvas.width, this.buffer.canvas.height);\r\n\r\n  };\r\n\r\n  this.render = function() { 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); };\r\n\r\n  this.resize = function(event) {\r\n\r\n    var height, width;\r\n\r\n    height = document.documentElement.clientHeight;\r\n    width  = document.documentElement.clientWidth;\r\n\r\n    this.context.canvas.height = height - 32;\r\n    this.context.canvas.width = width - 32;\r\n\r\n    this.render();\r\n\r\n  };\r\n\r\n  this.handleResize = (event) => { this.resize(event); };\r\n\r\n};\r\n\r\nDisplay.prototype = {\r\n\r\n  constructor : Display\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/01/engine-01.js",
    "content": "// 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 game state is updated at the same rate across different devices which is important\r\nfor uniform gameplay. Imagine playing your favorite game on a new phone and suddenly\r\nit's running at a different speed. That would be a bad user experience, so we fix\r\nit with a fixed step game loop. In addition, you can do things like frame dropping\r\nand interpolation with a fixed step loop, which allow your game to play and look\r\nsmooth on slower devices rather than freezing or lagging to the point of unplayability. */\r\n\r\nconst Engine = function(time_step, update, render) {\r\n\r\n  this.accumulated_time        = 0;// Amount of time that's accumulated since the last update.\r\n  this.animation_frame_request = undefined,// reference to the AFR\r\n  this.time                    = undefined,// The most recent timestamp of loop execution.\r\n  this.time_step               = time_step,// 1000/30 = 30 frames per second\r\n\r\n  this.updated = false;// Whether or not the update function has been called since the last cycle.\r\n\r\n  this.update = update;// The update function\r\n  this.render = render;// The render function\r\n\r\n  this.run = function(time_stamp) {// This is one cycle of the game loop\r\n\r\n    this.accumulated_time += time_stamp - this.time;\r\n    this.time = time_stamp;\r\n\r\n    /* If the device is too slow, updates may take longer than our time step. If\r\n    this is the case, it could freeze the game and overload the cpu. To prevent this,\r\n    we catch a memory spiral early and never allow three full frames to pass without\r\n    an update. This is not ideal, but at least the user won't crash their cpu. */\r\n    if (this.accumulated_time >= this.time_step * 3) {\r\n\r\n      this.accumulated_time = this.time_step;\r\n\r\n    }\r\n\r\n    /* Since we can only update when the screen is ready to draw and requestAnimationFrame\r\n    calls the run function, we need to keep track of how much time has passed. We\r\n    store that accumulated time and test to see if enough has passed to justify\r\n    an update. Remember, we want to update every time we have accumulated one time step's\r\n    worth of time, and if multiple time steps have accumulated, we must update one\r\n    time for each of them to stay up to speed. */\r\n    while(this.accumulated_time >= this.time_step) {\r\n\r\n      this.accumulated_time -= this.time_step;\r\n\r\n      this.update(time_stamp);\r\n\r\n      this.updated = true;// If the game has updated, we need to draw it again.\r\n\r\n    }\r\n\r\n    /* This allows us to only draw when the game has updated. */\r\n    if (this.updated) {\r\n\r\n      this.updated = false;\r\n      this.render(time_stamp);\r\n\r\n    }\r\n\r\n    this.animation_frame_request = window.requestAnimationFrame(this.handleRun);\r\n\r\n  };\r\n\r\n  this.handleRun = (time_step) => { this.run(time_step); };\r\n\r\n};\r\n\r\nEngine.prototype = {\r\n\r\n  constructor:Engine,\r\n\r\n  start:function() {\r\n\r\n    this.accumulated_time = this.time_step;\r\n    this.time = window.performance.now();\r\n    this.animation_frame_request = window.requestAnimationFrame(this.handleRun);\r\n\r\n  },\r\n\r\n  stop:function() { window.cancelAnimationFrame(this.animation_frame_request); }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/01/game-01.js",
    "content": "// 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 some color values which then get drawn to the display canvas in the loop. */\r\n\r\nconst Game = function() {\r\n\r\n  this.color  = \"rgb(0,0,0)\";\r\n  this.colors = [0, 0, 0];\r\n  this.shifts = [1, 1, 1];\r\n\r\n  this.update = function() {\r\n\r\n    for (let index = 0; index < 3; index ++) {\r\n\r\n      let color = this.colors[index];\r\n      let shift = this.shifts[index];\r\n\r\n      if (color + shift > 255 || color + shift < 0) {\r\n\r\n        shift = (shift < 0) ? Math.floor(Math.random() * 2) + 1 : Math.floor(Math.random() * -2) - 1;\r\n\r\n      }\r\n\r\n      color += shift;\r\n\r\n      this.colors[index] = color;\r\n      this.shifts[index] = shift;\r\n\r\n    }\r\n\r\n    this.color = \"rgb(\" + this.colors[0] + \",\" + this.colors[1] + \",\" + this.colors[2] + \")\";\r\n\r\n  };\r\n\r\n};\r\n\r\nGame.prototype = {\r\n\r\n  constructor : Game\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/01/main-01.js",
    "content": "// 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 controller, display, and game logic. It also has an engine which combines the\r\nthree logical parts which are otherwise completely separate. One of the most important\r\naspects of programming is organization. Without an organized foundation, your code\r\nwill quickly become unruly and difficult to maintain. Separating code into logical\r\ngroups is also a principle of object oriented programming, which lends itself to\r\ncomprehensible, maintainable code as well as modularity. */\r\n\r\n/* Since I am loading my scripts dynamically from the rabbit-trap.html, I am wrapping\r\nmy main JavaScript file in a load listener. This ensures that this code will not\r\nexecute until the document has finished loading and I have access to all of my classes. */\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  var render = function() {\r\n\r\n    display.renderColor(game.color);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    game.update();\r\n\r\n  };\r\n\r\n        /////////////////\r\n      //// OBJECTS ////\r\n    /////////////////\r\n\r\n    /* Usually I just write my logical sections into object literals, but the temptation\r\n    to reference one inside of another is too great, and leads to sloppy coding.\r\n    In an effort to attain cleaner code, I have written classes for each section\r\n    and instantiate them here. */\r\n\r\n    /* The controller handles user input. */\r\n    var controller = new Controller();\r\n    /* The display handles window resizing, as well as the on screen canvas. */\r\n    var display    = new Display(document.querySelector(\"canvas\"));\r\n    /* The game will eventually hold our game logic. */\r\n    var game       = new Game();\r\n    /* The engine is where the above three sections can interact. */\r\n    var engine     = new Engine(1000/30, render, update);\r\n\r\n        ////////////////////\r\n      //// INITIALIZE ////\r\n    ////////////////////\r\n\r\n    window.addEventListener(\"resize\",  display.handleResize);\r\n    window.addEventListener(\"keydown\", controller.handleKeyDownUp);\r\n    window.addEventListener(\"keyup\",   controller.handleKeyDownUp);\r\n\r\n    display.resize();\r\n    engine.start();\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/02/controller-02.js",
    "content": "// 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\r\n  this.left  = new Controller.ButtonInput();\r\n  this.right = new Controller.ButtonInput();\r\n  this.up    = new Controller.ButtonInput();\r\n\r\n  this.keyDownUp = function(type, key_code) {\r\n\r\n    var down = (type == \"keydown\") ? true : false;\r\n\r\n    switch(key_code) {\r\n\r\n      case 37: this.left.getInput(down);  break;\r\n      case 38: this.up.getInput(down);    break;\r\n      case 39: this.right.getInput(down);\r\n\r\n    }\r\n\r\n  };\r\n\r\n};\r\n\r\nController.prototype = {\r\n\r\n  constructor : Controller\r\n\r\n};\r\n\r\nController.ButtonInput = function() {\r\n\r\n  this.active = this.down = false;\r\n\r\n};\r\n\r\nController.ButtonInput.prototype = {\r\n\r\n  constructor : Controller.ButtonInput,\r\n\r\n  getInput : function(down) {\r\n\r\n    if (this.down != down) this.active = down;\r\n    this.down = down;\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/02/display-02.js",
    "content": "// 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 differently and draw rectangles to the buffer. */\r\n\r\nconst Display = function(canvas) {\r\n\r\n  this.buffer  = document.createElement(\"canvas\").getContext(\"2d\"),\r\n  this.context = canvas.getContext(\"2d\");\r\n\r\n  this.drawRectangle = function(x, y, width, height, color) {\r\n\r\n    this.buffer.fillStyle = color;\r\n    this.buffer.fillRect(Math.floor(x), Math.floor(y), width, height);\r\n\r\n  };\r\n\r\n  this.fill = function(color) {\r\n\r\n    this.buffer.fillStyle = color;\r\n    this.buffer.fillRect(0, 0, this.buffer.canvas.width, this.buffer.canvas.height);\r\n\r\n  };\r\n\r\n  this.render = function() { 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); };\r\n\r\n  this.resize = function(width, height, height_width_ratio) {\r\n\r\n    if (height / width > height_width_ratio) {\r\n\r\n      this.context.canvas.height = width * height_width_ratio;\r\n      this.context.canvas.width = width;\r\n\r\n    } else {\r\n\r\n      this.context.canvas.height = height;\r\n      this.context.canvas.width = height / height_width_ratio;\r\n\r\n    }\r\n\r\n    this.context.imageSmoothingEnabled = false;\r\n\r\n  };\r\n\r\n};\r\n\r\nDisplay.prototype = {\r\n\r\n  constructor : Display\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/02/game-02.js",
    "content": "// 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 that controls the virtual game world. Players, NPCs, world dimensions, collision\r\nmaps, and everything to do with the game world are stored in the world object. */\r\n\r\nconst Game = function() {\r\n\r\n  this.world = {\r\n\r\n    background_color:\"rgba(40,48,56,0.25)\",\r\n\r\n    friction:0.9,\r\n    gravity:3,\r\n\r\n    player:new Game.Player(),\r\n\r\n    height:72,\r\n    width:128,\r\n\r\n    collideObject:function(object) {\r\n\r\n      if (object.x < 0) { object.x = 0; object.velocity_x = 0; }\r\n      else if (object.x + object.width > this.width) { object.x = this.width - object.width; object.velocity_x = 0; }\r\n      if (object.y < 0) { object.y = 0; object.velocity_y = 0; }\r\n      else if (object.y + object.height > this.height) { object.jumping = false; object.y = this.height - object.height; object.velocity_y = 0; }\r\n\r\n    },\r\n\r\n    update:function() {\r\n\r\n      this.player.velocity_y += this.gravity;\r\n      this.player.update();\r\n\r\n      this.player.velocity_x *= this.friction;\r\n      this.player.velocity_y *= this.friction;\r\n\r\n      this.collideObject(this.player);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  this.update = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\n\r\nGame.prototype = { constructor : Game };\r\n\r\nGame.Player = function(x, y) {\r\n\r\n  this.color      = \"#ff0000\";\r\n  this.height     = 16;\r\n  this.jumping    = true;\r\n  this.velocity_x = 0;\r\n  this.velocity_y = 0;\r\n  this.width      = 16;\r\n  this.x          = 100;\r\n  this.y          = 50;\r\n\r\n};\r\n\r\nGame.Player.prototype = {\r\n\r\n  constructor : Game.Player,\r\n\r\n  jump:function() {\r\n\r\n    if (!this.jumping) {\r\n\r\n      this.color = \"#\" + Math.floor(Math.random() * 16777216).toString(16);// Change to random color\r\n      /* toString(16) will not add a leading 0 to a hex value, so this: #0fffff, for example,\r\n      isn't valid. toString would cut off the first 0. The code below inserts it. */\r\n      if (this.color.length != 7) {\r\n\r\n        this.color = this.color.slice(0, 1) + \"0\" + this.color.slice(1, 6);\r\n\r\n      }\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 20;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft:function()  { this.velocity_x -= 0.5; },\r\n  moveRight:function() { this.velocity_x += 0.5; },\r\n\r\n  update:function() {\r\n\r\n    this.x += this.velocity_x;\r\n    this.y += this.velocity_y;\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/02/main-02.js",
    "content": "// 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 collision detection, and I added keyboard input to move the\r\nplayer around. His color changes whenever he jumps. */\r\n\r\n/* Changes since the last part: I moved the event handlers out of the component\r\nfiles. Any functionality that might need two or more components to communicate\r\nshould be in the main file. The resize function, for example, shouldn't be in\r\nDisplay, because it needs information from Game to size the onscreen canvas.\r\nAs a general rule, anything that causes two components to communicate should be\r\nin the main file because we want to reduce internal references between components\r\nas much as possible. They should communicate via public methods that take primitives. */\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  /* This used to be in the Controller class, but I moved it out to the main file.\r\n  The reason being that later on in development I might need to do something with\r\n  display or processing directly on an input event in addition to updating the controller.\r\n  To prevent referencing those components inside of my controller logic, I moved\r\n  all of my event handlers here, to the main file. */\r\n  var keyDownUp = function(event) {\r\n\r\n    controller.keyDownUp(event.type, event.keyCode);\r\n\r\n  };\r\n\r\n  /* I also moved this handler out of Display since part 1 of this series. The reason\r\n  being that I need to reference game as well as display to resize the canvas according\r\n  to the dimensions of the game world. I don't want to reference game inside of my\r\n  Display class, so I moved the resize method into the main file. */\r\n  var resize = function(event) {\r\n\r\n    display.resize(document.documentElement.clientWidth - 32, document.documentElement.clientHeight - 32, game.world.height / game.world.width);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var render = function() {\r\n\r\n    display.fill(game.world.background_color);// Clear background to game's background color.\r\n    display.drawRectangle(game.world.player.x, game.world.player.y, game.world.player.width, game.world.player.height, game.world.player.color);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    if (controller.left.active)  { game.world.player.moveLeft();  }\r\n    if (controller.right.active) { game.world.player.moveRight(); }\r\n    if (controller.up.active)    { game.world.player.jump(); controller.up.active = false; }\r\n\r\n    game.update();\r\n\r\n  };\r\n\r\n      /////////////////\r\n    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var controller = new Controller();\r\n  var display    = new Display(document.querySelector(\"canvas\"));\r\n  var game       = new Game();\r\n  var engine     = new Engine(1000/30, render, update);\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  /* This is very important. The buffer canvas must be pixel for pixel the same\r\n  size as the world dimensions to properly scale the graphics. All the game knows\r\n  are player location and world dimensions. We have to tell the display to match them. */\r\n  display.buffer.canvas.height = game.world.height;\r\n  display.buffer.canvas.width = game.world.width;\r\n\r\n  window.addEventListener(\"keydown\", keyDownUp);\r\n  window.addEventListener(\"keyup\",   keyDownUp);\r\n  window.addEventListener(\"resize\",  resize);\r\n\r\n  resize();\r\n\r\n  engine.start();\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/03/display-03.js",
    "content": "// Frank Poth 03/23/2018\r\n\r\n/* I moved some generic functions to the Display.prototype.\r\nI created the Display.TileSheet class, which handles the tile sheet image and its\r\ndimensions.\r\nI got rid of the drawRectangle function and replaced it with the drawPlayer function. */\r\n\r\nconst Display = function(canvas) {\r\n\r\n  this.buffer  = document.createElement(\"canvas\").getContext(\"2d\"),\r\n  this.context = canvas.getContext(\"2d\");\r\n\r\n  this.tile_sheet = new Display.TileSheet(16, 8);\r\n\r\n  /* This function draws the map to the buffer. */\r\n  this.drawMap = function(map, columns) {\r\n\r\n    for (let index = map.length - 1; index > -1; -- index) {\r\n\r\n      let value = map[index] - 1;\r\n      let source_x =           (value % this.tile_sheet.columns) * this.tile_sheet.tile_size;\r\n      let source_y = Math.floor(value / this.tile_sheet.columns) * this.tile_sheet.tile_size;\r\n      let destination_x =           (index % columns) * this.tile_sheet.tile_size;\r\n      let destination_y = Math.floor(index / columns) * this.tile_sheet.tile_size;\r\n\r\n      this.buffer.drawImage(this.tile_sheet.image, source_x, source_y, this.tile_sheet.tile_size, this.tile_sheet.tile_size, destination_x, destination_y, this.tile_sheet.tile_size, this.tile_sheet.tile_size);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  this.drawPlayer = function(rectangle, color1, color2) {\r\n\r\n    this.buffer.fillStyle = color1;\r\n    this.buffer.fillRect(Math.floor(rectangle.x), Math.floor(rectangle.y), rectangle.width, rectangle.height);\r\n    this.buffer.fillStyle = color2;\r\n    this.buffer.fillRect(Math.floor(rectangle.x + 2), Math.floor(rectangle.y + 2), rectangle.width - 4, rectangle.height - 4);\r\n\r\n  };\r\n\r\n  this.resize = function(width, height, height_width_ratio) {\r\n\r\n    if (height / width > height_width_ratio) {\r\n\r\n      this.context.canvas.height = width * height_width_ratio;\r\n      this.context.canvas.width = width;\r\n\r\n    } else {\r\n\r\n      this.context.canvas.height = height;\r\n      this.context.canvas.width = height / height_width_ratio;\r\n\r\n    }\r\n\r\n    this.context.imageSmoothingEnabled = false;\r\n\r\n  };\r\n\r\n};\r\n\r\nDisplay.prototype = {\r\n\r\n  constructor : Display,\r\n\r\n  render:function() { 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); },\r\n\r\n};\r\n\r\nDisplay.TileSheet = function(tile_size, columns) {\r\n\r\n  this.image = new Image();\r\n  this.tile_size = tile_size;\r\n  this.columns = columns;\r\n\r\n};\r\n\r\nDisplay.TileSheet.prototype = {};\r\n"
  },
  {
    "path": "content/rabbit-trap/03/game-03.js",
    "content": "// 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 of Game.World. I am doing this in order to compartmentalize my objects more\r\naccurately. The Player class will never be used outside of the World class, and the\r\nWorld class will never be used outside of the Game class, therefore the classes will\r\nbe nested: Game -> Game.World -> Game.World.Player */\r\n\r\nconst Game = function() {\r\n\r\n  /* The world object is now its own class. */\r\n  this.world = new Game.World();\r\n\r\n  /* The Game.update function works the same as in part 2. */\r\n  this.update = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\n\r\nGame.prototype = { constructor : Game };\r\n\r\n/* The world is now its own class. */\r\nGame.World = function(friction = 0.9, gravity = 3) {\r\n\r\n  this.friction = friction;\r\n  this.gravity  = gravity;\r\n\r\n  /* Player is now its own class inside of the Game.World object. */\r\n  this.player   = new Game.World.Player();\r\n\r\n  /* Here is the map data. Later on I will load it from a json file, but for now\r\n  I will just hardcode it here. */\r\n  this.columns   = 12;\r\n  this.rows      = 9;\r\n  this.tile_size = 16;\r\n  this.map = [49,18,18,18,50,49,19,20,17,18,36,37,\r\n              11,40,40,40,17,19,40,32,32,32,40,08,\r\n              11,32,40,32,32,32,40,13,06,06,29,02,\r\n              36,07,40,40,32,40,40,20,40,40,09,10,\r\n              03,32,32,48,40,48,40,32,32,05,37,26,\r\n              11,40,40,32,40,40,40,32,32,32,40,38,\r\n              11,40,32,05,15,07,40,40,04,40,01,43,\r\n              50,03,32,32,12,40,40,32,12,01,43,10,\r\n              09,41,28,14,38,28,14,04,23,35,10,25];\r\n\r\n  /* Height and Width now depend on the map size. */\r\n  this.height   = this.tile_size * this.rows;\r\n  this.width    = this.tile_size * this.columns;\r\n\r\n};\r\n\r\n/* Now that world is a class, I moved its more generic functions into its prototype. */\r\nGame.World.prototype = {\r\n\r\n  constructor: Game.World,\r\n\r\n  collideObject:function(object) {// Same as in part 2.\r\n\r\n    if (object.x < 0) { object.x = 0; object.velocity_x = 0; }\r\n    else if (object.x + object.width > this.width) { object.x = this.width - object.width; object.velocity_x = 0; }\r\n    if (object.y < 0) { object.y = 0; object.velocity_y = 0; }\r\n    else if (object.y + object.height > this.height) { object.jumping = false; object.y = this.height - object.height; object.velocity_y = 0; }\r\n\r\n  },\r\n\r\n  update:function() {\r\n\r\n    this.player.velocity_y += this.gravity;\r\n    this.player.update();\r\n\r\n    this.player.velocity_x *= this.friction;\r\n    this.player.velocity_y *= this.friction;\r\n\r\n    this.collideObject(this.player);\r\n\r\n  }\r\n\r\n};\r\n\r\n/* The player is also its own class now. Since player only appears in the context\r\nof Game.World, that is where it is defined. */\r\nGame.World.Player = function(x, y) {\r\n\r\n  this.color1     = \"#404040\";\r\n  this.color2     = \"#f0f0f0\";\r\n  this.height     = 12;\r\n  this.jumping    = true;\r\n  this.velocity_x = 0;\r\n  this.velocity_y = 0;\r\n  this.width      = 12;\r\n  this.x          = 100;\r\n  this.y          = 50;\r\n\r\n};\r\n\r\nGame.World.Player.prototype = {\r\n\r\n  constructor : Game.World.Player,\r\n\r\n  jump:function() {\r\n\r\n    if (!this.jumping) {\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 20;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft:function()  { this.velocity_x -= 0.5; },\r\n  moveRight:function() { this.velocity_x += 0.5; },\r\n\r\n  update:function() {\r\n\r\n    this.x += this.velocity_x;\r\n    this.y += this.velocity_y;\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/03/main-03.js",
    "content": "// Frank Poth 03/23/2017\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  var keyDownUp = function(event) {\r\n\r\n    controller.keyDownUp(event.type, event.keyCode);\r\n\r\n  };\r\n\r\n  var resize = function(event) {\r\n\r\n    display.resize(document.documentElement.clientWidth - 32, document.documentElement.clientHeight - 32, game.world.height / game.world.width);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var render = function() {\r\n\r\n    display.drawMap(game.world.map, game.world.columns);\r\n    display.drawPlayer(game.world.player, game.world.player.color1, game.world.player.color2);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    if (controller.left.active)  { game.world.player.moveLeft();  }\r\n    if (controller.right.active) { game.world.player.moveRight(); }\r\n    if (controller.up.active)    { game.world.player.jump(); controller.up.active = false; }\r\n\r\n    game.update();\r\n\r\n  };\r\n\r\n      /////////////////\r\n    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var controller = new Controller();\r\n  var display    = new Display(document.querySelector(\"canvas\"));\r\n  var game       = new Game();\r\n  var engine     = new Engine(1000/30, render, update);\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.buffer.canvas.height = game.world.height;\r\n  display.buffer.canvas.width = game.world.width;\r\n\r\n  display.tile_sheet.image.addEventListener(\"load\", function(event) {\r\n\r\n    resize();\r\n\r\n    engine.start();\r\n\r\n  }, { once:true });\r\n\r\n  display.tile_sheet.image.src = \"rabbit-trap.png\";\r\n\r\n  window.addEventListener(\"keydown\", keyDownUp);\r\n  window.addEventListener(\"keyup\",   keyDownUp);\r\n  window.addEventListener(\"resize\",  resize);\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/04/display-04.js",
    "content": "// 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 drawing tiles from the game object's map. Each value used to be offset\r\nby 1 due to the export format of my tile map editor. I also changed the rounding\r\nmethod in the drawPlayer function from Math.floor to Math.round to better represent\r\nwhere the player is actually standing. */\r\n\r\nconst Display = function(canvas) {\r\n\r\n  this.buffer  = document.createElement(\"canvas\").getContext(\"2d\"),\r\n  this.context = canvas.getContext(\"2d\");\r\n\r\n  this.tile_sheet = new Display.TileSheet(16, 8);\r\n\r\n  /* This function draws the map to the buffer. */\r\n  this.drawMap = function(map, columns) {\r\n\r\n    for (let index = map.length - 1; index > -1; -- index) {\r\n\r\n      let value = map[index]; // No longer subtracting 1. The values in my tile map have been shifted down by 1.\r\n      let source_x =           (value % this.tile_sheet.columns) * this.tile_sheet.tile_size;\r\n      let source_y = Math.floor(value / this.tile_sheet.columns) * this.tile_sheet.tile_size;\r\n      let destination_x =           (index % columns) * this.tile_sheet.tile_size;\r\n      let destination_y = Math.floor(index / columns) * this.tile_sheet.tile_size;\r\n\r\n      this.buffer.drawImage(this.tile_sheet.image, source_x, source_y, this.tile_sheet.tile_size, this.tile_sheet.tile_size, destination_x, destination_y, this.tile_sheet.tile_size, this.tile_sheet.tile_size);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  this.drawPlayer = function(rectangle, color1, color2) {\r\n\r\n    this.buffer.fillStyle = color1;\r\n    this.buffer.fillRect(Math.round(rectangle.x), Math.round(rectangle.y), rectangle.width, rectangle.height);\r\n    this.buffer.fillStyle = color2;\r\n    this.buffer.fillRect(Math.round(rectangle.x + 2), Math.round(rectangle.y + 2), rectangle.width - 4, rectangle.height - 4);\r\n\r\n  };\r\n\r\n  this.resize = function(width, height, height_width_ratio) {\r\n\r\n    if (height / width > height_width_ratio) {\r\n\r\n      this.context.canvas.height = width * height_width_ratio;\r\n      this.context.canvas.width = width;\r\n\r\n    } else {\r\n\r\n      this.context.canvas.height = height;\r\n      this.context.canvas.width = height / height_width_ratio;\r\n\r\n    }\r\n\r\n    this.context.imageSmoothingEnabled = false;\r\n\r\n  };\r\n\r\n};\r\n\r\nDisplay.prototype = {\r\n\r\n  constructor : Display,\r\n\r\n  render:function() { 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); },\r\n\r\n};\r\n\r\nDisplay.TileSheet = function(tile_size, columns) {\r\n\r\n  this.image = new Image();\r\n  this.tile_size = tile_size;\r\n  this.columns = columns;\r\n\r\n};\r\n\r\nDisplay.TileSheet.prototype = {};\r\n"
  },
  {
    "path": "content/rabbit-trap/04/game-04.js",
    "content": "// 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 tile map offset from part 3, where every graphical value was offset by\r\n1 due to the export format of the tile map editor I used. I added the collision_map\r\nand the collider object to handle collision. I also added a superclass called Object\r\nthat all other game objects will extend. It has a bunch of methods for working with\r\nobject position. */\r\n\r\nconst Game = function() {\r\n\r\n  this.world = new Game.World();// All the changes are in the world class.\r\n\r\n  this.update = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\n\r\nGame.prototype = { constructor : Game };\r\n\r\nGame.World = function(friction = 0.9, gravity = 3) {\r\n\r\n  this.collider = new Game.World.Collider();// Here's the new collider class.\r\n\r\n  this.friction = friction;\r\n  this.gravity  = gravity;\r\n\r\n  this.player   = new Game.World.Player();\r\n\r\n  this.columns   = 12;\r\n  this.rows      = 9;\r\n  this.tile_size = 16;\r\n\r\n  /* This map stays the same. It is the graphical map. It only places graphics and\r\n  has nothing to do with collision. */\r\n  this.map = [48,17,17,17,49,48,18,19,16,17,35,36,\r\n              10,39,39,39,16,18,39,31,31,31,39,07,\r\n              10,31,39,31,31,31,39,12,05,05,28,01,\r\n              35,06,39,39,31,39,39,19,39,39,08,09,\r\n              02,31,31,47,39,47,39,31,31,04,36,25,\r\n              10,39,39,31,39,39,39,31,31,31,39,37,\r\n              10,39,31,04,14,06,39,39,03,39,00,42,\r\n              49,02,31,31,11,39,39,31,11,00,42,09,\r\n              08,40,27,13,37,27,13,03,22,34,09,24];\r\n\r\n  /* These collision values correspond to collision functions in the Collider class.\r\n  00 is nothing. everything else is run through a switch statement and routed to the\r\n  appropriate collision functions. These particular values aren't arbitrary. Their binary\r\n  representation can be used to describe which sides of the tile have boundaries.\r\n\r\n  0000 = 0  tile 0:    0    tile 1:   1     tile 2:    0    tile 15:    1\r\n  0001 = 1           0   0          0   0            0   1            1   1\r\n  0010 = 2             0              0                0                1\r\n  1111 = 15        No walls     Wall on top      Wall on Right      four walls\r\n\r\n  This binary representation can be used to describe which sides of a tile are boundaries.\r\n  Each bit represents a side: 0 0 0 0 = l b r t (left bottom right top). Keep in mind\r\n  that this is just one way to look at it. You could assign your collision values\r\n  any way you want. This is just the way I chose to keep track of which values represent\r\n  which tiles. I haven't tested this representation approach with more advanced shapes. */\r\n\r\n  this.collision_map = [00,04,04,04,00,00,04,04,04,04,04,00,\r\n                        02,00,00,00,12,06,00,00,00,00,00,08,\r\n                        02,00,00,00,00,00,00,09,05,05,01,00,\r\n                        00,07,00,00,00,00,00,14,00,00,08,00,\r\n                        02,00,00,01,00,01,00,00,00,13,04,00,\r\n                        02,00,00,00,00,00,00,00,00,00,00,08,\r\n                        02,00,00,13,01,07,00,00,11,00,09,00,\r\n                        00,03,00,00,10,00,00,00,08,01,00,00,\r\n                        00,00,01,01,00,01,01,01,00,00,00,00];\r\n\r\n  this.height   = this.tile_size * this.rows;\r\n  this.width    = this.tile_size * this.columns;\r\n\r\n};\r\n\r\nGame.World.prototype = {\r\n\r\n  constructor: Game.World,\r\n\r\n  /* This function has been hugely modified. */\r\n  collideObject:function(object) {\r\n\r\n    /* Let's make sure we can't leave the world boundaries. */\r\n    if      (object.getLeft()   < 0          ) { object.setLeft(0);             object.velocity_x = 0; }\r\n    else if (object.getRight()  > this.width ) { object.setRight(this.width);   object.velocity_x = 0; }\r\n    if      (object.getTop()    < 0          ) { object.setTop(0);              object.velocity_y = 0; }\r\n    else if (object.getBottom() > this.height) { object.setBottom(this.height); object.velocity_y = 0; object.jumping = false; }\r\n\r\n    /* Now let's collide with some tiles!!! The side values refer to the tile grid\r\n    row and column spaces that the object is occupying on each of its sides. For\r\n    instance bottom refers to the row in the collision map that the bottom of the\r\n    object occupies. Right refers to the column in the collision map occupied by\r\n    the right side of the object. Value refers to the value of a collision tile in\r\n    the map under the specified row and column occupied by the object. */\r\n    var bottom, left, right, top, value;\r\n\r\n    /* First we test the top left corner of the object. We get the row and column\r\n    he occupies in the collision map, then we get the value from the collision map\r\n    at that row and column. In this case the row is top and the column is left. Then\r\n    we hand the information to the collider's collide function. */\r\n    top    = Math.floor(object.getTop()    / this.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_size);\r\n    value  = this.collision_map[top * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_size, top * this.tile_size, this.tile_size);\r\n\r\n    /* We must redifine top since the last collision check because the object may\r\n    have moved since the last collision check. Also, the reason I check the top corners\r\n    first is because if the object is moved down while checking the top, he will be\r\n    moved back up when checking the bottom, and it is better to look like he is standing\r\n    on the ground than being pushed down through the ground by the cieling. */\r\n    top    = Math.floor(object.getTop()    / this.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_size);\r\n    value  = this.collision_map[top * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_size, top * this.tile_size, this.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_size, bottom * this.tile_size, this.tile_size);\r\n\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_size, bottom * this.tile_size, this.tile_size);\r\n\r\n  },\r\n\r\n  update:function() {\r\n\r\n    this.player.velocity_y += this.gravity;\r\n    this.player.update();\r\n\r\n    this.player.velocity_x *= this.friction;\r\n    this.player.velocity_y *= this.friction;\r\n\r\n    this.collideObject(this.player);\r\n\r\n  }\r\n\r\n};\r\n\r\nGame.World.Collider = function() {\r\n\r\n  /* This is the function routing method. Basically, you know what the tile looks like\r\n  from its value. You know which object you want to collide with, and you know the\r\n  x and y position of the tile as well as its dimensions. This function just decides\r\n  which collision functions to use based on the value and allows you to tweak the\r\n  other values to fit the specific tile shape. */\r\n  this.collide = function(value, object, tile_x, tile_y, tile_size) {\r\n\r\n    switch(value) { // which value does our tile have?\r\n\r\n      /* All 15 tile types can be described with only 4 collision methods. These\r\n      methods are mixed and matched for each unique tile. */\r\n\r\n      case  1: this.collidePlatformTop      (object, tile_y            ); break;\r\n      case  2: this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case  3: if (this.collidePlatformTop  (object, tile_y            )) return;// If there's a collision, we don't need to check for anything else.\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case  4: this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  5: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  6: if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  7: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  8: this.collidePlatformLeft     (object, tile_x            ); break;\r\n      case  9: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               this.collidePlatformLeft     (object, tile_x            ); break;\r\n      case 10: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case 11: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case 12: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 13: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 14: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               if (this.collidePlatformRight(object, tile_x            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 15: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\n/* Here's where all of the collision functions live. */\r\nGame.World.Collider.prototype = {\r\n\r\n  constructor: Game.World.Collider,\r\n\r\n  /* This will resolve a collision (if any) between an object and the y location of\r\n  some tile's bottom. All of these functions are pretty much the same, just adjusted\r\n  for different sides of a tile and different trajectories of the object. */\r\n  collidePlatformBottom:function(object, tile_bottom) {\r\n\r\n    /* If the top of the object is above the bottom of the tile and on the previous\r\n    frame the top of the object was below the bottom of the tile, we have entered into\r\n    this tile. Pretty simple stuff. */\r\n    if (object.getTop() < tile_bottom && object.getOldTop() >= tile_bottom) {\r\n\r\n      object.setTop(tile_bottom);// Move the top of the object to the bottom of the tile.\r\n      object.velocity_y = 0;     // Stop moving in that direction.\r\n      return true;               // Return true because there was a collision.\r\n\r\n    } return false;              // Return false if there was no collision.\r\n\r\n  },\r\n\r\n  collidePlatformLeft:function(object, tile_left) {\r\n\r\n    if (object.getRight() > tile_left && object.getOldRight() <= tile_left) {\r\n\r\n      object.setRight(tile_left - 0.01);// -0.01 is to fix a small problem with rounding\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformRight:function(object, tile_right) {\r\n\r\n    if (object.getLeft() < tile_right && object.getOldLeft() >= tile_right) {\r\n\r\n      object.setLeft(tile_right);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformTop:function(object, tile_top) {\r\n\r\n    if (object.getBottom() > tile_top && object.getOldBottom() <= tile_top) {\r\n\r\n      object.setBottom(tile_top - 0.01);\r\n      object.velocity_y = 0;\r\n      object.jumping    = false;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  }\r\n\r\n };\r\n\r\n/* The object class is just a basic rectangle with a bunch of prototype functions\r\nto help us work with positioning this rectangle. */\r\nGame.World.Object = function(x, y, width, height) {\r\n\r\n this.height = height;\r\n this.width  = width;\r\n this.x      = x;\r\n this.x_old  = x;\r\n this.y      = y;\r\n this.y_old  = y;\r\n\r\n};\r\n\r\nGame.World.Object.prototype = {\r\n\r\n  constructor:Game.World.Object,\r\n\r\n  /* These functions are used to get and set the different side positions of the object. */\r\n  getBottom:   function()  { return this.y     + this.height; },\r\n  getLeft:     function()  { return this.x;                   },\r\n  getRight:    function()  { return this.x     + this.width;  },\r\n  getTop:      function()  { return this.y;                   },\r\n  getOldBottom:function()  { return this.y_old + this.height; },\r\n  getOldLeft:  function()  { return this.x_old;               },\r\n  getOldRight: function()  { return this.x_old + this.width;  },\r\n  getOldTop:   function()  { return this.y_old                },\r\n  setBottom:   function(y) { this.y     = y    - this.height; },\r\n  setLeft:     function(x) { this.x     = x;                  },\r\n  setRight:    function(x) { this.x     = x    - this.width;  },\r\n  setTop:      function(y) { this.y     = y;                  },\r\n  setOldBottom:function(y) { this.y_old = y    - this.height; },\r\n  setOldLeft:  function(x) { this.x_old = x;                  },\r\n  setOldRight: function(x) { this.x_old = x    - this.width;  },\r\n  setOldTop:   function(y) { this.y_old = y;                  }\r\n\r\n};\r\n\r\nGame.World.Player = function(x, y) {\r\n\r\n  Game.World.Object.call(this, 100, 100, 12, 12);\r\n\r\n  this.color1     = \"#404040\";\r\n  this.color2     = \"#f0f0f0\";\r\n\r\n  this.jumping    = true;\r\n  this.velocity_x = 0;\r\n  this.velocity_y = 0;\r\n\r\n};\r\n\r\nGame.World.Player.prototype = {\r\n\r\n  jump:function() {\r\n\r\n    if (!this.jumping) {\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 20;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft:function()  { this.velocity_x -= 0.5; },\r\n  moveRight:function() { this.velocity_x += 0.5; },\r\n\r\n  update:function() {\r\n\r\n    this.x_old = this.x;\r\n    this.y_old = this.y;\r\n    this.x    += this.velocity_x;\r\n    this.y    += this.velocity_y;\r\n\r\n  }\r\n\r\n};\r\n\r\nObject.assign(Game.World.Player.prototype, Game.World.Object.prototype);\r\nGame.World.Player.prototype.constructor = Game.World.Player;\r\n"
  },
  {
    "path": "content/rabbit-trap/05/display-05.js",
    "content": "// 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 class to Game.\r\n2. Changed the drawMap function to be as generic as posible.\r\n3. Changed the drawPlayer function to the drawObject function. */\r\n\r\nconst Display = function(canvas) {\r\n\r\n  this.buffer  = document.createElement(\"canvas\").getContext(\"2d\"),\r\n  this.context = canvas.getContext(\"2d\");\r\n\r\n  /* This function draws the map to the buffer. */\r\n  this.drawMap = function(image, image_columns, map, map_columns, tile_size) {\r\n\r\n    for (let index = map.length - 1; index > -1; -- index) {\r\n\r\n      let value         = map[index];\r\n      let source_x      =           (value % image_columns) * tile_size;\r\n      let source_y      = Math.floor(value / image_columns) * tile_size;\r\n      let destination_x =           (index % map_columns  ) * tile_size;\r\n      let destination_y = Math.floor(index / map_columns  ) * tile_size;\r\n\r\n      this.buffer.drawImage(image, source_x, source_y, tile_size, tile_size, destination_x, destination_y, tile_size, tile_size);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  this.drawObject = function(image, source_x, source_y, destination_x, destination_y, width, height) {\r\n\r\n    this.buffer.drawImage(image, source_x, source_y, width, height, Math.round(destination_x), Math.round(destination_y), width, height);\r\n\r\n  };\r\n\r\n  this.resize = function(width, height, height_width_ratio) {\r\n\r\n    if (height / width > height_width_ratio) {\r\n\r\n      this.context.canvas.height = width * height_width_ratio;\r\n      this.context.canvas.width  = width;\r\n\r\n    } else {\r\n\r\n      this.context.canvas.height = height;\r\n      this.context.canvas.width  = height / height_width_ratio;\r\n\r\n    }\r\n\r\n    this.context.imageSmoothingEnabled = false;\r\n\r\n  };\r\n\r\n};\r\n\r\nDisplay.prototype = {\r\n\r\n  constructor : Display,\r\n\r\n  render:function() { 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); },\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/05/game-05.js",
    "content": "// 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.\r\n2. Added the Game.World.Object.Animator class.\r\n3. Updated the Player object to include frame sets for his animations.\r\n   Updated the Player to update position and animation separately.\r\n4. Removed tile_size from the world object and only use tile_size from the tile_set object.\r\n5. Moved Game.World.Player to Game.World.Object.Player.\r\n6. Changed Game.World.prototype.update to better handle player animation updates.\r\n\r\nI'm starting to realize that perhaps the nesting constructor naming convension is\r\ngetting out of hand. Game.World.Object.Animator is a very long and confusing constructor.\r\nI may just move everything under Game so everything is still namespaced, but things won't\r\nhave super long ridiculous constructors. However, the long constructor is still useful\r\nto determine where a class is pertinent and what it's purpose is. Still, it feels very\r\nclunky, and I will probably change it soon. */\r\n\r\n/* 04/11/2018 I noticed a problem with tunneling in the lower right nook of the T\r\non the floor of the level. If you run into from the right and jump up, the player\r\nseemingly moves through the wall. This is not a problem with tile collision, but rather,\r\ntunneling. His jump velocity moves him upwards more than one full tile space. */\r\n\r\nconst Game = function() {\r\n\r\n  this.world    = new Game.World();\r\n\r\n  this.update   = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\n\r\nGame.prototype = {\r\n\r\n  constructor : Game,\r\n\r\n};\r\n\r\nGame.World = function(friction = 0.8, gravity = 2) {\r\n\r\n  this.collider = new Game.World.Collider();\r\n\r\n  this.friction = friction;\r\n  this.gravity  = gravity;\r\n\r\n  this.columns   = 12;\r\n  this.rows      = 9;\r\n\r\n  /* Here's where I define the new TileSet class. I give it complete control over\r\n  tile_size because there should only be one source for tile_size for both drawing\r\n  and collision as this game won't use scaling on individual objects. */\r\n  this.tile_set = new Game.World.TileSet(8, 16);\r\n  this.player   = new Game.World.Object.Player(100, 100);// The player in its new \"namespace\".\r\n\r\n  this.map = [48,17,17,17,49,48,18,19,16,17,35,36,\r\n              10,39,39,39,16,18,39,31,31,31,39,07,\r\n              10,31,39,31,31,31,39,12,05,05,28,01,\r\n              35,06,39,39,31,39,39,19,39,39,08,09,\r\n              02,31,31,47,39,47,39,31,31,04,36,25,\r\n              10,39,39,31,39,39,39,31,31,31,39,37,\r\n              10,39,31,04,14,06,39,39,03,39,00,42,\r\n              49,02,31,31,11,39,39,31,11,00,42,09,\r\n              08,40,27,13,37,27,13,03,22,34,09,24];\r\n\r\n  this.collision_map = [00,04,04,04,00,00,04,04,04,04,04,00,\r\n                        02,00,00,00,12,06,00,00,00,00,00,08,\r\n                        02,00,00,00,00,00,00,09,05,05,01,00,\r\n                        00,07,00,00,00,00,00,14,00,00,08,00,\r\n                        02,00,00,01,00,01,00,00,00,13,04,00,\r\n                        02,00,00,00,00,00,00,00,00,00,00,08,\r\n                        02,00,00,13,01,07,00,00,11,00,09,00,\r\n                        00,03,00,00,10,00,00,00,08,01,00,00,\r\n                        00,00,01,01,00,01,01,01,00,00,00,00];\r\n\r\n  this.height   = this.tile_set.tile_size * this.rows;   // these changed to use tile_set.tile_size\r\n  this.width    = this.tile_set.tile_size * this.columns;// I got rid of this.tile_size in Game.World\r\n\r\n};\r\n\r\nGame.World.prototype = {\r\n\r\n  constructor: Game.World,\r\n\r\n  collideObject:function(object) {\r\n\r\n    if      (object.getLeft()   < 0          ) { object.setLeft(0);             object.velocity_x = 0; }\r\n    else if (object.getRight()  > this.width ) { object.setRight(this.width);   object.velocity_x = 0; }\r\n    if      (object.getTop()    < 0          ) { object.setTop(0);              object.velocity_y = 0; }\r\n    else if (object.getBottom() > this.height) { object.setBottom(this.height); object.velocity_y = 0; object.jumping = false; }\r\n\r\n    var bottom, left, right, top, value;\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n  },\r\n\r\n  /* This function changed to update the player's position and then do collision,\r\n  and then update the animation based on the player's final condition. */\r\n  update:function() {\r\n\r\n    this.player.updatePosition(this.gravity, this.friction);\r\n\r\n    this.collideObject(this.player);\r\n\r\n    this.player.updateAnimation();\r\n\r\n  }\r\n\r\n};\r\n\r\nGame.World.Collider = function() {\r\n\r\n  this.collide = function(value, object, tile_x, tile_y, tile_size) {\r\n\r\n    switch(value) {\r\n\r\n      case  1: this.collidePlatformTop      (object, tile_y            ); break;\r\n      case  2: this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case  3: if (this.collidePlatformTop  (object, tile_y            )) return;// If there's a collision, we don't need to check for anything else.\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case  4: this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  5: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  6: if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  7: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case  8: this.collidePlatformLeft     (object, tile_x            ); break;\r\n      case  9: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               this.collidePlatformLeft     (object, tile_x            ); break;\r\n      case 10: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case 11: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformRight    (object, tile_x + tile_size); break;\r\n      case 12: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 13: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 14: if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               if (this.collidePlatformRight(object, tile_x + tile_size)) return; // Had to change this since part 4. I forgot to add tile_size\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n      case 15: if (this.collidePlatformTop  (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft (object, tile_x            )) return;\r\n               if (this.collidePlatformRight(object, tile_x + tile_size)) return;\r\n               this.collidePlatformBottom   (object, tile_y + tile_size); break;\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\nGame.World.Collider.prototype = {\r\n\r\n  constructor: Game.World.Collider,\r\n\r\n  collidePlatformBottom:function(object, tile_bottom) {\r\n\r\n    if (object.getTop() < tile_bottom && object.getOldTop() >= tile_bottom) {\r\n\r\n      object.setTop(tile_bottom);\r\n      object.velocity_y = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformLeft:function(object, tile_left) {\r\n\r\n    if (object.getRight() > tile_left && object.getOldRight() <= tile_left) {\r\n\r\n      object.setRight(tile_left - 0.01);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformRight:function(object, tile_right) {\r\n\r\n    if (object.getLeft() < tile_right && object.getOldLeft() >= tile_right) {\r\n\r\n      object.setLeft(tile_right);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformTop:function(object, tile_top) {\r\n\r\n    if (object.getBottom() > tile_top && object.getOldBottom() <= tile_top) {\r\n\r\n      object.setBottom(tile_top - 0.01);\r\n      object.velocity_y = 0;\r\n      object.jumping    = false;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  }\r\n\r\n };\r\n\r\nGame.World.Object = function(x, y, width, height) {\r\n\r\n this.height = height;\r\n this.width  = width;\r\n this.x      = x;\r\n this.x_old  = x;\r\n this.y      = y;\r\n this.y_old  = y;\r\n\r\n};\r\n\r\nGame.World.Object.prototype = {\r\n\r\n  constructor:Game.World.Object,\r\n\r\n  /* These functions are used to get and set the different side positions of the object. */\r\n  getBottom:   function()  { return this.y     + this.height; },\r\n  getLeft:     function()  { return this.x;                   },\r\n  getRight:    function()  { return this.x     + this.width;  },\r\n  getTop:      function()  { return this.y;                   },\r\n  getOldBottom:function()  { return this.y_old + this.height; },\r\n  getOldLeft:  function()  { return this.x_old;               },\r\n  getOldRight: function()  { return this.x_old + this.width;  },\r\n  getOldTop:   function()  { return this.y_old                },\r\n  setBottom:   function(y) { this.y     = y    - this.height; },\r\n  setLeft:     function(x) { this.x     = x;                  },\r\n  setRight:    function(x) { this.x     = x    - this.width;  },\r\n  setTop:      function(y) { this.y     = y;                  },\r\n  setOldBottom:function(y) { this.y_old = y    - this.height; },\r\n  setOldLeft:  function(x) { this.x_old = x;                  },\r\n  setOldRight: function(x) { this.x_old = x    - this.width;  },\r\n  setOldTop:   function(y) { this.y_old = y;                  }\r\n\r\n};\r\n\r\nGame.World.Object.Animator = function(frame_set, delay) {\r\n\r\n  this.count       = 0;\r\n  this.delay       = (delay >= 1) ? delay : 1;\r\n  this.frame_set   = frame_set;\r\n  this.frame_index = 0;\r\n  this.frame_value = frame_set[0];\r\n  this.mode        = \"pause\";\r\n\r\n};\r\n\r\nGame.World.Object.Animator.prototype = {\r\n\r\n  constructor:Game.World.Object.Animator,\r\n\r\n  animate:function() {\r\n\r\n    switch(this.mode) {\r\n\r\n      case \"loop\" : this.loop(); break;\r\n      case \"pause\":              break;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {\r\n\r\n    if (this.frame_set === frame_set) { return; }\r\n\r\n    this.count       = 0;\r\n    this.delay       = delay;\r\n    this.frame_set   = frame_set;\r\n    this.frame_index = frame_index;\r\n    this.frame_value = frame_set[frame_index];\r\n    this.mode        = mode;\r\n\r\n  },\r\n\r\n  loop:function() {\r\n\r\n    this.count ++;\r\n\r\n    while(this.count > this.delay) {\r\n\r\n      this.count -= this.delay;\r\n\r\n      this.frame_index = (this.frame_index < this.frame_set.length - 1) ? this.frame_index + 1 : 0;\r\n\r\n      this.frame_value = this.frame_set[this.frame_index];\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\n/* The player now also extends the Game.World.Object.Animator class. I also added\r\na direction_x variable to help determine which way the player is facing for animation. */\r\nGame.World.Object.Player = function(x, y) {\r\n\r\n  Game.World.Object.call(this, 100, 100, 7, 14);\r\n  Game.World.Object.Animator.call(this, Game.World.Object.Player.prototype.frame_sets[\"idle-left\"], 10);\r\n\r\n  this.jumping     = true;\r\n  this.direction_x = -1;\r\n  this.velocity_x  = 0;\r\n  this.velocity_y  = 0;\r\n\r\n};\r\n\r\nGame.World.Object.Player.prototype = {\r\n\r\n  constructor:Game.World.Object.Player,\r\n\r\n  /* The values in these arrays correspond to the TileSet.Frame objects in the tile_set.\r\n  They are just hardcoded in here now, but when the tileset information is eventually\r\n  loaded from a json file, this will be allocated dynamically in some sort of loading function. */\r\n  frame_sets: {\r\n\r\n    \"idle-left\" : [0],\r\n    \"jump-left\" : [1],\r\n    \"move-left\" : [2, 3, 4, 5],\r\n    \"idle-right\": [6],\r\n    \"jump-right\": [7],\r\n    \"move-right\": [8, 9, 10, 11]\r\n\r\n  },\r\n\r\n  jump: function() {\r\n\r\n    if (!this.jumping) {\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 20;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft: function() {\r\n\r\n    this.direction_x = -1;// Make sure to set the player's direction.\r\n    this.velocity_x -= 0.55;\r\n\r\n  },\r\n\r\n  moveRight:function(frame_set) {\r\n\r\n    this.direction_x = 1;\r\n    this.velocity_x += 0.55;\r\n\r\n  },\r\n\r\n  /* Because animation is entirely dependent on the player's movement at this point,\r\n  I made a separate update function just for animation to be called after collision\r\n  between the player and the world. This gives the most accurate animations for what\r\n  the player is doing movement wise on the screen. */\r\n  updateAnimation:function() {\r\n\r\n    if (this.velocity_y < 0) {\r\n\r\n      if (this.direction_x < 0) this.changeFrameSet(this.frame_sets[\"jump-left\"], \"pause\");\r\n      else this.changeFrameSet(this.frame_sets[\"jump-right\"], \"pause\");\r\n\r\n    } else if (this.direction_x < 0) {\r\n\r\n      if (this.velocity_x < -0.1) this.changeFrameSet(this.frame_sets[\"move-left\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-left\"], \"pause\");\r\n\r\n    } else if (this.direction_x > 0) {\r\n\r\n      if (this.velocity_x > 0.1) this.changeFrameSet(this.frame_sets[\"move-right\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-right\"], \"pause\");\r\n\r\n    }\r\n\r\n    this.animate();\r\n\r\n  },\r\n\r\n  /* This used to be the update function, but now it's a little bit better. It takes\r\n  gravity and friction as parameters so the player class can decide what to do with\r\n  them. */\r\n  updatePosition:function(gravity, friction) {// Changed from the update function\r\n\r\n    this.x_old = this.x;\r\n    this.y_old = this.y;\r\n    this.velocity_y += gravity;\r\n    this.x    += this.velocity_x;\r\n    this.y    += this.velocity_y;\r\n\r\n    this.velocity_x *= friction;\r\n    this.velocity_y *= friction;\r\n\r\n  }\r\n\r\n};\r\n\r\n/* Double prototype inheritance from Object and Animator. */\r\nObject.assign(Game.World.Object.Player.prototype, Game.World.Object.prototype);\r\nObject.assign(Game.World.Object.Player.prototype, Game.World.Object.Animator.prototype);\r\nGame.World.Object.Player.prototype.constructor = Game.World.Object.Player;\r\n\r\n/* The TileSheet class was taken from the Display class and renamed TileSet.\r\nIt does all the same stuff, but it doesn't have an image reference and it also\r\ndefines specific regions in the tile set image that correspond to the player's sprite\r\nanimation frames. Later, this will all be set in a level loading function just in case\r\nI want to add functionality to add in another tile sheet graphic with different terrain. */\r\nGame.World.TileSet = function(columns, tile_size) {\r\n\r\n  this.columns    = columns;\r\n  this.tile_size  = tile_size;\r\n\r\n  let f = Game.World.TileSet.Frame;\r\n\r\n  /* An array of all the frames in the tile sheet image. */\r\n  this.frames = [new f(115,  96, 13, 16, 0, -2), // idle-left\r\n                 new f( 50,  96, 13, 16, 0, -2), // jump-left\r\n                 new f(102,  96, 13, 16, 0, -2), new f(89, 96, 13, 16, 0, -2), new f(76, 96, 13, 16, 0, -2), new f(63, 96, 13, 16, 0, -2), // walk-left\r\n                 new f(  0, 112, 13, 16, 0, -2), // idle-right\r\n                 new f( 65, 112, 13, 16, 0, -2), // jump-right\r\n                 new f( 13, 112, 13, 16, 0, -2), new f(26, 112, 13, 16, 0, -2), new f(39, 112, 13, 16, 0, -2), new f(52, 112, 13, 16, 0, -2) // walk-right\r\n                ];\r\n\r\n};\r\n\r\nGame.World.TileSet.prototype = { constructor: Game.World.TileSet };\r\n\r\n/* The Frame class just defines a region in a tilesheet to cut out. It's a rectangle.\r\nIt has an x and y offset used for drawing the cut out sprite image to the screen,\r\nwhich allows sprites to be positioned anywhere in the tile sheet image rather than\r\nbeing forced to adhere to a grid like tile graphics. This is more natural because\r\nsprites often fluctuate in size and won't always fit in a 16x16 grid. */\r\nGame.World.TileSet.Frame = function(x, y, width, height, offset_x, offset_y) {\r\n\r\n  this.x        = x;\r\n  this.y        = y;\r\n  this.width    = width;\r\n  this.height   = height;\r\n  this.offset_x = offset_x;\r\n  this.offset_y = offset_y;\r\n\r\n};\r\n\r\nGame.World.TileSet.Frame.prototype = { constructor: Game.World.TileSet.Frame };\r\n"
  },
  {
    "path": "content/rabbit-trap/05/main-05.js",
    "content": "// 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 and sounds.\r\n2. The render function now draws the player's frame instead of a square like in part 4.\r\n3. The resize function now stretches the display canvas to the full viewport capacity.\r\n\r\nThe project is starting to grow unmanagable as it grows. Luckily my IPO structure\r\nis dramatically decreasing the amount of rewrites I have to do to other classes,\r\nbut since most of my code is in the Game class, edits in that class are becoming\r\nrather tedious. As the project grows I will have to focus my videos more on individual\r\nchanges and ignore the vast bulk of existing code. */\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n  //// CLASSES ////\r\n\r\n  /* The assets manager will be responsible for loading and storing graphics for\r\n  the game. Because it only has to load the tilesheet image right now, it's very specific\r\n  about what it does. */\r\n  const AssetsManager = function() {\r\n\r\n    this.tile_set_image = undefined;\r\n\r\n  };\r\n\r\n  AssetsManager.prototype = {\r\n\r\n    constructor: Game.AssetsManager,\r\n\r\n    loadTileSetImage:function(url, callback) {\r\n\r\n      this.tile_set_image = new Image();\r\n\r\n      this.tile_set_image.addEventListener(\"load\", function(event) {\r\n\r\n        callback();\r\n\r\n      }, { once : true});\r\n\r\n      this.tile_set_image.src = url;\r\n\r\n    }\r\n\r\n  };\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  var keyDownUp = function(event) {\r\n\r\n    controller.keyDownUp(event.type, event.keyCode);\r\n\r\n  };\r\n\r\n  var resize = function(event) {\r\n\r\n    display.resize(document.documentElement.clientWidth, document.documentElement.clientHeight, game.world.height / game.world.width);\r\n    display.render();\r\n\r\n  };\r\n\r\n  /* The render function uses the new display methods now. I will eventually have to create\r\n  some sort of object manager when I get more objects on the screen. */\r\n  var render = function() {\r\n\r\n    display.drawMap   (assets_manager.tile_set_image,\r\n    game.world.tile_set.columns, game.world.map, game.world.columns,  game.world.tile_set.tile_size);\r\n\r\n    let frame = game.world.tile_set.frames[game.world.player.frame_value];\r\n\r\n    display.drawObject(assets_manager.tile_set_image,\r\n    frame.x, frame.y,\r\n    game.world.player.x + Math.floor(game.world.player.width * 0.5 - frame.width * 0.5) + frame.offset_x,\r\n    game.world.player.y + frame.offset_y, frame.width, frame.height);\r\n\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    if (controller.left.active ) { game.world.player.moveLeft ();                               }\r\n    if (controller.right.active) { game.world.player.moveRight();                               }\r\n    if (controller.up.active   ) { game.world.player.jump();      controller.up.active = false; }\r\n\r\n    game.update();\r\n\r\n  };\r\n\r\n      /////////////////\r\n    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var assets_manager = new AssetsManager();// Behold the new assets manager!\r\n  var controller     = new Controller();\r\n  var display        = new Display(document.querySelector(\"canvas\"));\r\n  var game           = new Game();\r\n  var engine         = new Engine(1000/30, render, update);\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  /* This is going to have to be moved to a setup function inside of the Display class or something.\r\n  Leaving it out here is kind of sloppy. */\r\n  display.buffer.canvas.height = game.world.height;\r\n  display.buffer.canvas.width  = game.world.width;\r\n  display.buffer.imageSmoothingEnabled = false;\r\n\r\n  /* Now my image is loaded into the assets manager instead of the display object.\r\n  The callback starts the game engine when the graphic is loaded. */\r\n  assets_manager.loadTileSetImage(\"rabbit-trap.png\", () => {\r\n\r\n    resize();\r\n    engine.start();\r\n\r\n  });\r\n\r\n  window.addEventListener(\"keydown\", keyDownUp);\r\n  window.addEventListener(\"keyup\",   keyDownUp);\r\n  window.addEventListener(\"resize\",  resize);\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/06/engine-06.js",
    "content": "// 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\nwindow.requestAnimationFrame before I update my game logic, I called it after. This\r\nmeant that even if I tried to stop my game loop from inside the game logic, RAF would\r\nstill be called after I requested a stop. To fix this, I simply moved the request\r\nto the top of my game loop function, which is Engine.run. */\r\n\r\nconst Engine = function(time_step, update, render) {\r\n\r\n  this.accumulated_time        = 0;\r\n  this.animation_frame_request = undefined,\r\n  this.time                    = undefined,\r\n  this.time_step               = time_step,\r\n\r\n  this.updated = false;\r\n\r\n  this.update = update;\r\n  this.render = render;\r\n\r\n  this.run = function(time_stamp) {\r\n\r\n    /* I moved this line from the bottom of this function to the top. This is better\r\n    anyway, because it ensures that if my game logic runs too long, a new frame will\r\n    already be requested before 30 or 60 frames pass and I miss a request entirely.\r\n    This could cause a \"spiral of death\" for my CPU, but since I have the frame dropping\r\n    safety if statement, this probably won't crash the user's computer. */\r\n    this.animation_frame_request = window.requestAnimationFrame(this.handleRun);\r\n\r\n    this.accumulated_time += time_stamp - this.time;\r\n    this.time = time_stamp;\r\n\r\n    /* This is the safety if statement. */\r\n    if (this.accumulated_time >= this.time_step * 3) {\r\n\r\n      this.accumulated_time = this.time_step;\r\n\r\n    }\r\n\r\n    while(this.accumulated_time >= this.time_step) {\r\n\r\n      this.accumulated_time -= this.time_step;\r\n\r\n      this.update(time_stamp);\r\n\r\n      this.updated = true;\r\n\r\n    }\r\n\r\n    if (this.updated) {\r\n\r\n      this.updated = false;\r\n      this.render(time_stamp);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  this.handleRun = (time_step) => { this.run(time_step); };\r\n\r\n};\r\n\r\nEngine.prototype = {\r\n\r\n  constructor:Engine,\r\n\r\n  start:function() {\r\n\r\n    this.accumulated_time = this.time_step;\r\n    this.time = window.performance.now();\r\n    this.animation_frame_request = window.requestAnimationFrame(this.handleRun);\r\n\r\n  },\r\n\r\n  stop:function() { window.cancelAnimationFrame(this.animation_frame_request); }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/06/game-06.js",
    "content": "// 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: For example:\r\n     Game.World.Object.Player is now Game.Player.\r\n  2. Added Game.World.prototype.setup to setup world from json level data.\r\n  3. Added the Game.MovingObject class to separate Objects from MovingObjects.\r\n     Game.Player now inherits from MovingObject instead of Object.\r\n  4. Changed Game.World.map to Game.World.graphical_map.\r\n  5. Made the Game.Collider.collideObject routing function do all y first collision checks.\r\n     This simply means that I check collision on top and bottom before left and right.\r\n  6. Removed world boundary collision from World.collideObject so the player can\r\n     move off screen enough to hit a door.\r\n  7. Added the Game.Door class.\r\n  8. Added functions to get the center position of Game.Object and Game.MovingObject.\r\n  9. Organized classes by alphabeticalish order.\r\n  10. Put a limit on player velocity because there was a problem with \"tunneling\"\r\n      through tiles due to jump movement speed.\r\n  11. Changed the player's hitbox size and his frame offsets for animation.\r\n\r\n*/\r\n\r\nconst Game = function() {\r\n\r\n  this.world    = new Game.World();\r\n\r\n  this.update   = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\nGame.prototype = { constructor : Game };\r\n\r\nGame.Animator = function(frame_set, delay) {\r\n\r\n this.count       = 0;\r\n this.delay       = (delay >= 1) ? delay : 1;\r\n this.frame_set   = frame_set;\r\n this.frame_index = 0;\r\n this.frame_value = frame_set[0];\r\n this.mode        = \"pause\";\r\n\r\n};\r\nGame.Animator.prototype = {\r\n\r\n constructor:Game.Animator,\r\n\r\n animate:function() {\r\n\r\n   switch(this.mode) {\r\n\r\n     case \"loop\" : this.loop(); break;\r\n     case \"pause\":              break;\r\n\r\n   }\r\n\r\n },\r\n\r\n changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {\r\n\r\n   if (this.frame_set === frame_set) { return; }\r\n\r\n   this.count       = 0;\r\n   this.delay       = delay;\r\n   this.frame_set   = frame_set;\r\n   this.frame_index = frame_index;\r\n   this.frame_value = frame_set[frame_index];\r\n   this.mode        = mode;\r\n\r\n },\r\n\r\n loop:function() {\r\n\r\n   this.count ++;\r\n\r\n   while(this.count > this.delay) {\r\n\r\n     this.count -= this.delay;\r\n\r\n     this.frame_index = (this.frame_index < this.frame_set.length - 1) ? this.frame_index + 1 : 0;\r\n\r\n     this.frame_value = this.frame_set[this.frame_index];\r\n\r\n   }\r\n\r\n }\r\n\r\n};\r\n\r\nGame.Collider = function() {\r\n\r\n  /* I changed this so all the checks happen in y first order. */\r\n  this.collide = function(value, object, tile_x, tile_y, tile_size) {\r\n\r\n    switch(value) {\r\n\r\n      case  1:     this.collidePlatformTop    (object, tile_y            ); break;\r\n      case  2:     this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  3: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  4:     this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  5: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  6: if (this.collidePlatformRight  (object, tile_x + tile_size)) return;\r\n                   this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  7: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  8:     this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case  9: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 10: if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 11: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 12: if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 13: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 14: if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 15: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\nGame.Collider.prototype = {\r\n\r\n  constructor: Game.Collider,\r\n\r\n  collidePlatformBottom:function(object, tile_bottom) {\r\n\r\n    if (object.getTop() < tile_bottom && object.getOldTop() >= tile_bottom) {\r\n\r\n      object.setTop(tile_bottom);\r\n      object.velocity_y = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformLeft:function(object, tile_left) {\r\n\r\n    if (object.getRight() > tile_left && object.getOldRight() <= tile_left) {\r\n\r\n      object.setRight(tile_left - 0.01);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformRight:function(object, tile_right) {\r\n\r\n    if (object.getLeft() < tile_right && object.getOldLeft() >= tile_right) {\r\n\r\n      object.setLeft(tile_right);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformTop:function(object, tile_top) {\r\n\r\n    if (object.getBottom() > tile_top && object.getOldBottom() <= tile_top) {\r\n\r\n      object.setBottom(tile_top - 0.01);\r\n      object.velocity_y = 0;\r\n      object.jumping    = false;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  }\r\n\r\n };\r\n\r\nGame.Frame = function(x, y, width, height, offset_x, offset_y) {\r\n\r\n  this.x        = x;\r\n  this.y        = y;\r\n  this.width    = width;\r\n  this.height   = height;\r\n  this.offset_x = offset_x;\r\n  this.offset_y = offset_y;\r\n\r\n};\r\nGame.Frame.prototype = { constructor: Game.Frame };\r\n\r\nGame.Object = function(x, y, width, height) {\r\n\r\n this.height = height;\r\n this.width  = width;\r\n this.x      = x;\r\n this.y      = y;\r\n\r\n};\r\n/* I added getCenterX, getCenterY, setCenterX, and setCenterY */\r\nGame.Object.prototype = {\r\n\r\n  constructor:Game.Object,\r\n\r\n  getBottom : function()  { return this.y + this.height;       },\r\n  getCenterX: function()  { return this.x + this.width  * 0.5; },\r\n  getCenterY: function()  { return this.y + this.height * 0.5; },\r\n  getLeft   : function()  { return this.x;                     },\r\n  getRight  : function()  { return this.x + this.width;        },\r\n  getTop    : function()  { return this.y;                     },\r\n  setBottom : function(y) { this.y = y - this.height;          },\r\n  setCenterX: function(x) { this.x = x - this.width  * 0.5;    },\r\n  setCenterY: function(y) { this.y = y - this.height * 0.5;    },\r\n  setLeft   : function(x) { this.x = x;                        },\r\n  setRight  : function(x) { this.x = x - this.width;           },\r\n  setTop    : function(y) { this.y = y;                        }\r\n\r\n};\r\n\r\nGame.MovingObject = function(x, y, width, height, velocity_max = 15) {\r\n\r\n  Game.Object.call(this, x, y, width, height);\r\n\r\n  this.jumping      = false;\r\n  this.velocity_max = velocity_max;// added velocity_max so velocity can't go past 16\r\n  this.velocity_x   = 0;\r\n  this.velocity_y   = 0;\r\n  this.x_old        = x;\r\n  this.y_old        = y;\r\n\r\n};\r\n/* I added setCenterX, setCenterY, getCenterX, and getCenterY */\r\nGame.MovingObject.prototype = {\r\n\r\n  getOldBottom : function()  { return this.y_old + this.height;       },\r\n  getOldCenterX: function()  { return this.x_old + this.width  * 0.5; },\r\n  getOldCenterY: function()  { return this.y_old + this.height * 0.5; },\r\n  getOldLeft   : function()  { return this.x_old;                     },\r\n  getOldRight  : function()  { return this.x_old + this.width;        },\r\n  getOldTop    : function()  { return this.y_old;                     },\r\n  setOldBottom : function(y) { this.y_old = y    - this.height;       },\r\n  setOldCenterX: function(x) { this.x_old = x    - this.width  * 0.5; },\r\n  setOldCenterY: function(y) { this.y_old = y    - this.height * 0.5; },\r\n  setOldLeft   : function(x) { this.x_old = x;                        },\r\n  setOldRight  : function(x) { this.x_old = x    - this.width;        },\r\n  setOldTop    : function(y) { this.y_old = y;                        }\r\n\r\n};\r\nObject.assign(Game.MovingObject.prototype, Game.Object.prototype);\r\nGame.MovingObject.prototype.constructor = Game.MovingObject;\r\n\r\nGame.Door = function(door) {\r\n\r\n Game.Object.call(this, door.x, door.y, door.width, door.height);\r\n\r\n this.destination_x    = door.destination_x;\r\n this.destination_y    = door.destination_y;\r\n this.destination_zone = door.destination_zone;\r\n\r\n};\r\nGame.Door.prototype = {\r\n\r\n /* Tests for collision between this door object and a MovingObject. */\r\n collideObject(object) {\r\n\r\n   let center_x = object.getCenterX();\r\n   let center_y = object.getCenterY();\r\n\r\n   if (center_x < this.getLeft() || center_x > this.getRight() ||\r\n       center_y < this.getTop()  || center_y > this.getBottom()) return false;\r\n\r\n   return true;\r\n\r\n }\r\n\r\n};\r\nObject.assign(Game.Door.prototype, Game.Object.prototype);\r\nGame.Door.prototype.constructor = Game.Door;\r\n\r\nGame.Player = function(x, y) {\r\n\r\n  Game.MovingObject.call(this, x, y, 7, 12);\r\n  Game.Animator.call(this, Game.Player.prototype.frame_sets[\"idle-left\"], 10);\r\n\r\n  this.jumping     = true;\r\n  this.direction_x = -1;\r\n  this.velocity_x  = 0;\r\n  this.velocity_y  = 0;\r\n\r\n};\r\nGame.Player.prototype = {\r\n\r\n  frame_sets: {\r\n\r\n    \"idle-left\" : [0],\r\n    \"jump-left\" : [1],\r\n    \"move-left\" : [2, 3, 4, 5],\r\n    \"idle-right\": [6],\r\n    \"jump-right\": [7],\r\n    \"move-right\": [8, 9, 10, 11]\r\n\r\n  },\r\n\r\n  jump: function() {\r\n\r\n    /* Made it so you can only jump if you aren't falling faster than 10px per frame. */\r\n    if (!this.jumping && this.velocity_y < 10) {\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 13;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft: function() {\r\n\r\n    this.direction_x = -1;\r\n    this.velocity_x -= 0.55;\r\n\r\n  },\r\n\r\n  moveRight:function(frame_set) {\r\n\r\n    this.direction_x = 1;\r\n    this.velocity_x += 0.55;\r\n\r\n  },\r\n\r\n  updateAnimation:function() {\r\n\r\n    if (this.velocity_y < 0) {\r\n\r\n      if (this.direction_x < 0) this.changeFrameSet(this.frame_sets[\"jump-left\"], \"pause\");\r\n      else this.changeFrameSet(this.frame_sets[\"jump-right\"], \"pause\");\r\n\r\n    } else if (this.direction_x < 0) {\r\n\r\n      if (this.velocity_x < -0.1) this.changeFrameSet(this.frame_sets[\"move-left\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-left\"], \"pause\");\r\n\r\n    } else if (this.direction_x > 0) {\r\n\r\n      if (this.velocity_x > 0.1) this.changeFrameSet(this.frame_sets[\"move-right\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-right\"], \"pause\");\r\n\r\n    }\r\n\r\n    this.animate();\r\n\r\n  },\r\n\r\n  updatePosition:function(gravity, friction) {\r\n\r\n    this.x_old = this.x;\r\n    this.y_old = this.y;\r\n\r\n    this.velocity_y += gravity;\r\n    this.velocity_x *= friction;\r\n\r\n    /* Made it so that velocity cannot exceed velocity_max */\r\n    if (Math.abs(this.velocity_x) > this.velocity_max)\r\n    this.velocity_x = this.velocity_max * Math.sign(this.velocity_x);\r\n\r\n    if (Math.abs(this.velocity_y) > this.velocity_max)\r\n    this.velocity_y = this.velocity_max * Math.sign(this.velocity_y);\r\n\r\n    this.x    += this.velocity_x;\r\n    this.y    += this.velocity_y;\r\n\r\n  }\r\n\r\n};\r\nObject.assign(Game.Player.prototype, Game.MovingObject.prototype);\r\nObject.assign(Game.Player.prototype, Game.Animator.prototype);\r\nGame.Player.prototype.constructor = Game.Player;\r\n\r\nGame.TileSet = function(columns, tile_size) {\r\n\r\n  this.columns    = columns;\r\n  this.tile_size  = tile_size;\r\n\r\n  let f = Game.Frame;\r\n\r\n  this.frames = [new f(115,  96, 13, 16, 0, -4), // idle-left\r\n                 new f( 50,  96, 13, 16, 0, -4), // jump-left\r\n                 new f(102,  96, 13, 16, 0, -4), new f(89, 96, 13, 16, 0, -4), new f(76, 96, 13, 16, 0, -4), new f(63, 96, 13, 16, 0, -4), // walk-left\r\n                 new f(  0, 112, 13, 16, 0, -4), // idle-right\r\n                 new f( 65, 112, 13, 16, 0, -4), // jump-right\r\n                 new f( 13, 112, 13, 16, 0, -4), new f(26, 112, 13, 16, 0, -4), new f(39, 112, 13, 16, 0, -4), new f(52, 112, 13, 16, 0, -4) // walk-right\r\n                ];\r\n\r\n};\r\nGame.TileSet.prototype = { constructor: Game.TileSet };\r\n\r\nGame.World = function(friction = 0.85, gravity = 2) {\r\n\r\n  this.collider  = new Game.Collider();\r\n\r\n  this.friction  = friction;\r\n  this.gravity   = gravity;\r\n\r\n  this.columns   = 12;\r\n  this.rows      = 9;\r\n\r\n  this.tile_set  = new Game.TileSet(8, 16);\r\n  this.player    = new Game.Player(32, 76);\r\n\r\n  this.zone_id   = \"00\";// The current zone.\r\n\r\n  this.doors     = [];// The array of doors in the level.\r\n  this.door      = undefined; // If the player enters a door, the game will set this property to that door and the level will be loaded.\r\n\r\n  this.height    = this.tile_set.tile_size * this.rows;\r\n  this.width     = this.tile_set.tile_size * this.columns;\r\n\r\n};\r\nGame.World.prototype = {\r\n\r\n  constructor: Game.World,\r\n\r\n  collideObject:function(object) {\r\n\r\n    /* I got rid of the world boundary collision. Now it's up to the tiles to keep\r\n    the player from falling out of the world. */\r\n\r\n    var bottom, left, right, top, value;\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n  },\r\n\r\n  /* The setup function takes a zone object generated from a zoneXX.json file. It\r\n  sets all the world values to values of zone. If the player just passed through a\r\n  door, it uses the this.door variable to change the player's location to wherever\r\n  that door's destination goes. */\r\n  setup:function(zone) {\r\n\r\n    /* Get the new tile maps, the new zone, and reset the doors array. */\r\n    this.graphical_map      = zone.graphical_map;\r\n    this.collision_map      = zone.collision_map;\r\n    this.columns            = zone.columns;\r\n    this.rows               = zone.rows;\r\n    this.doors              = new Array();\r\n    this.zone_id            = zone.id;\r\n\r\n    /* Generate new doors. */\r\n    for (let index = zone.doors.length - 1; index > -1; -- index) {\r\n\r\n      let door = zone.doors[index];\r\n      this.doors[index] = new Game.Door(door);\r\n\r\n    }\r\n\r\n    /* If the player entered into a door, this.door will reference that door. Here\r\n    it will be used to set the player's location to the door's destination. */\r\n    if (this.door) {\r\n\r\n      /* if a destination is equal to -1, that means it won't be used. Since each zone\r\n      spans from 0 to its width and height, any negative number would be invalid. If\r\n      a door's destination is -1, the player will keep his current position for that axis. */\r\n      if (this.door.destination_x != -1) {\r\n\r\n        this.player.setCenterX   (this.door.destination_x);\r\n        this.player.setOldCenterX(this.door.destination_x);// It's important to reset the old position as well.\r\n\r\n      }\r\n\r\n      if (this.door.destination_y != -1) {\r\n\r\n        this.player.setCenterY   (this.door.destination_y);\r\n        this.player.setOldCenterY(this.door.destination_y);\r\n\r\n      }\r\n\r\n      this.door = undefined;// Make sure to reset this.door so we don't trigger a zone load.\r\n\r\n    }\r\n\r\n  },\r\n\r\n  update:function() {\r\n\r\n    this.player.updatePosition(this.gravity, this.friction);\r\n\r\n    this.collideObject(this.player);\r\n\r\n    /* Here we loop through all the doors in the current zone and check to see\r\n    if the player is colliding with any. If he does collide with one, we set the\r\n    world's door variable equal to that door, so we know to use it to load the next zone. */\r\n    for(let index = this.doors.length - 1; index > -1; -- index) {\r\n\r\n      let door = this.doors[index];\r\n\r\n      if (door.collideObject(this.player)) {\r\n\r\n        this.door = door;\r\n\r\n      };\r\n\r\n    }\r\n\r\n    this.player.updateAnimation();\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/06/main-06.js",
    "content": "// 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 door\r\n     is selected, the game engine stops and the door's level is loaded.\r\n  2. When the game is first initialized at the bottom of this file, game.world is\r\n     loaded using it's default values defined in its constructor.\r\n  3. The AssetsManager class has been changed to load both images and json.\r\n\r\n*/\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n  //// CONSTANTS ////\r\n\r\n  /* Each zone has a url that looks like: zoneXX.json, where XX is the current zone\r\n  identifier. When loading zones, I use the game.world's zone identifier with these\r\n  two constants to construct a url that points to the appropriate zone file. */\r\n  /* I updated this after I made the video. I decided to move the zone files into\r\n  the 06 folder because I won't be using these levels again in future parts. */\r\n  const ZONE_PREFIX = \"06/zone\";\r\n  const ZONE_SUFFIX = \".json\";\r\n\r\n      /////////////////\r\n    //// CLASSES ////\r\n  /////////////////\r\n\r\n  const AssetsManager = function() {\r\n\r\n    this.tile_set_image = undefined;\r\n\r\n  };\r\n\r\n  AssetsManager.prototype = {\r\n\r\n    constructor: Game.AssetsManager,\r\n\r\n    /* Requests a file and hands the callback function the contents of that file\r\n    parsed by JSON.parse. */\r\n    requestJSON:function(url, callback) {\r\n\r\n      let request = new XMLHttpRequest();\r\n\r\n      request.addEventListener(\"load\", function(event) {\r\n\r\n        callback(JSON.parse(this.responseText));\r\n\r\n      }, { once:true });\r\n\r\n      request.open(\"GET\", url);\r\n      request.send();\r\n\r\n    },\r\n\r\n    /* Creates a new Image and sets its src attribute to the specified url. When\r\n    the image loads, the callback function is called. */\r\n    requestImage:function(url, callback) {\r\n\r\n      let image = new Image();\r\n\r\n      image.addEventListener(\"load\", function(event) {\r\n\r\n        callback(image);\r\n\r\n      }, { once:true });\r\n\r\n      image.src = url;\r\n\r\n    },\r\n\r\n  };\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  var keyDownUp = function(event) {\r\n\r\n    controller.keyDownUp(event.type, event.keyCode);\r\n\r\n  };\r\n\r\n  var resize = function(event) {\r\n\r\n    display.resize(document.documentElement.clientWidth, document.documentElement.clientHeight, game.world.height / game.world.width);\r\n    display.render();\r\n\r\n  };\r\n\r\n  var render = function() {\r\n\r\n    display.drawMap   (assets_manager.tile_set_image,\r\n    game.world.tile_set.columns, game.world.graphical_map, game.world.columns,  game.world.tile_set.tile_size);\r\n\r\n    let frame = game.world.tile_set.frames[game.world.player.frame_value];\r\n\r\n    display.drawObject(assets_manager.tile_set_image,\r\n    frame.x, frame.y,\r\n    game.world.player.x + Math.floor(game.world.player.width * 0.5 - frame.width * 0.5) + frame.offset_x,\r\n    game.world.player.y + frame.offset_y, frame.width, frame.height);\r\n\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    if (controller.left.active ) { game.world.player.moveLeft ();                               }\r\n    if (controller.right.active) { game.world.player.moveRight();                               }\r\n    if (controller.up.active   ) { game.world.player.jump();      controller.up.active = false; }\r\n\r\n    game.update();\r\n\r\n    /* This if statement checks to see if a door has been selected by the player.\r\n    If the player collides with a door, he selects it. The engine is then stopped\r\n    and the assets_manager loads the door's level. */\r\n    if (game.world.door) {\r\n\r\n      engine.stop();\r\n\r\n      /* Here I'm requesting the JSON file to use to populate the game.world object. */\r\n      assets_manager.requestJSON(ZONE_PREFIX + game.world.door.destination_zone + ZONE_SUFFIX, (zone) => {\r\n\r\n        game.world.setup(zone);\r\n\r\n        engine.start();\r\n\r\n      });\r\n\r\n      return;\r\n\r\n    }\r\n\r\n  };\r\n\r\n      /////////////////\r\n    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var assets_manager = new AssetsManager();\r\n  var controller     = new Controller();\r\n  var display        = new Display(document.querySelector(\"canvas\"));\r\n  var game           = new Game();\r\n  var engine         = new Engine(1000/30, render, update);\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.buffer.canvas.height = game.world.height;\r\n  display.buffer.canvas.width  = game.world.width;\r\n  display.buffer.imageSmoothingEnabled = false;\r\n\r\n  assets_manager.requestJSON(ZONE_PREFIX + game.world.zone_id + ZONE_SUFFIX, (zone) => {\r\n\r\n    game.world.setup(zone);\r\n\r\n    assets_manager.requestImage(\"rabbit-trap.png\", (image) => {\r\n\r\n      assets_manager.tile_set_image = image;\r\n\r\n      resize();\r\n      engine.start();\r\n\r\n    });\r\n\r\n  });\r\n\r\n  window.addEventListener(\"keydown\", keyDownUp);\r\n  window.addEventListener(\"keyup\"  , keyDownUp);\r\n  window.addEventListener(\"resize\" , resize);\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/06/script.txt",
    "content": "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 platformer in pure HTML5 and JavaScript.\r\n\r\nThis video is going to be about loading JSON levels with doors. You're definitely\r\ngoing to want this functionality in your game so stay tuned to find out how it's done!\r\n\r\nBULLETS\r\n\r\n1. Example overview\r\n\r\n2. Create JSON files\r\n\r\n3. loading JSON files and using it\r\n  a. initial load\r\n  b. world.setup\r\n  c. loading levels from update\r\n\r\n4. trigger a level load by hitting a door\r\n  a. hit door check in the update function\r\n  b. setup loads the world and moves the player.\r\n\r\n  In this video I'm going to show you how to create level data files using JSON,\r\n  how to load level data and use it to populate the game world, and finally,\r\n  how to trigger subsequent level loads by colliding with doors.\r\n\r\n  If you have any comments or questions, be sure to leave them in the comments\r\n  section, and if you start learning at any point during this video, go ahead and\r\n  give a thumbs up so others can find it as well.\r\n"
  },
  {
    "path": "content/rabbit-trap/06/zone00.json",
    "content": "{\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, \"destination_y\":-1 }\r\n\r\n  ],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"graphical_map\":[42,24,17,17,17,17,26,24,17,35,5,44,24,18,31,39,39,31,29,18,39,31,39,11,10,39,39,39,39,31,11,31,39,39,0,41,10,39,31,47,39,4,21,31,39,39,16,17,43,6,39,31,31,39,31,39,47,39,39,31,38,31,31,31,7,39,31,39,31,39,39,12,32,6,31,3,7,7,39,39,39,47,39,37,10,7,12,23,13,7,3,31,31,31,39,8,40,1,41,1,41,1,41,1,1,1,1,48],\r\n\r\n  \"collision_map\":[0,0,4,4,4,4,0,0,4,4,4,0,0,6,0,0,0,0,8,6,0,0,0,8,2,0,0,0,0,0,10,0,0,0,9,0,2,0,0,1,0,13,6,0,0,0,12,4,0,7,0,0,0,0,0,0,1,0,0,0,2,0,0,0,11,0,0,0,0,0,0,9,0,3,0,9,0,3,0,0,0,1,0,8,0,0,1,0,0,0,3,0,0,0,0,8,0,0,0,0,0,0,0,1,1,1,1,0],\r\n\r\n  \"id\":\"00\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/06/zone01.json",
    "content": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":64, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"00\", \"destination_x\":192, \"destination_y\":-1 },\r\n\r\n    { \"x\":192, \"y\":16, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"02\", \"destination_x\":0, \"destination_y\":-1 }\r\n\r\n  ],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"graphical_map\":[30,16,49,2,7,7,22,36,30,19,16,17,11,39,16,49,2,4,21,31,19,39,31,31,38,31,31,16,18,31,31,39,31,39,39,4,18,31,31,31,31,39,39,31,31,47,31,0,31,39,39,31,31,39,39,39,31,31,31,8,5,5,14,6,31,39,39,47,39,31,31,8,1,27,21,39,39,31,31,39,39,12,28,42,9,10,31,39,39,3,39,39,7,37,42,24,26,40,1,1,2,11,0,27,14,34,9,10],\r\n\r\n  \"collision_map\":[0,4,0,0,0,0,0,4,0,4,4,4,2,0,12,0,0,4,6,0,14,0,0,0,2,0,0,12,6,0,0,0,0,0,0,9,6,0,0,0,0,0,0,0,0,1,0,8,0,0,0,0,0,0,0,0,0,0,0,8,1,1,1,7,0,0,0,1,0,0,0,8,0,0,6,0,0,0,0,0,0,9,1,0,0,2,0,0,0,11,0,0,9,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0],\r\n\r\n  \"id\":\"01\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/06/zone02.json",
    "content": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":16, \"width\":16, \"height\":16,\r\n      \"destination_zone\":\"01\", \"destination_x\":192, \"destination_y\":-1 },\r\n\r\n    { \"x\":32, \"y\":144, \"width\":80, \"height\":40,\r\n      \"destination_zone\":\"03\", \"destination_x\":-1, \"destination_y\":0 }\r\n\r\n  ],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"graphical_map\":[17,17,18,7,16,43,21,16,17,17,17,17,31,31,39,4,5,21,39,39,31,31,31,7,6,31,31,31,31,31,39,39,31,31,7,7,27,6,39,31,39,39,31,31,4,5,5,14,10,31,31,47,39,39,31,31,39,39,39,37,40,2,39,39,39,39,3,31,31,39,39,8,24,30,31,31,47,31,20,13,39,31,31,8,49,38,31,39,39,39,31,22,13,39,39,8,8,10,31,47,31,47,31,11,37,1,1,42],\r\n\r\n  \"collision_map\":[4,4,4,0,0,0,4,4,4,4,4,0,0,0,0,12,4,6,0,0,0,0,0,8,3,0,0,0,0,0,0,0,0,0,9,0,0,7,0,0,0,0,0,0,13,5,4,0,2,0,0,1,0,0,0,0,0,0,0,8,0,3,0,0,0,0,11,0,0,0,0,8,0,2,0,0,1,0,12,3,0,0,0,8,0,2,0,0,0,0,0,8,3,0,0,8,0,2,0,1,0,1,0,8,0,1,1,0],\r\n\r\n  \"id\":\"02\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/06/zone03.json",
    "content": "{\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, \"destination_y\":143 },\r\n\r\n    { \"x\":192, \"y\":80, \"width\":16, \"height\":64,\r\n      \"destination_zone\":\"04\", \"destination_x\":0, \"destination_y\":-1 }\r\n\r\n  ],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"graphical_map\":[16,18,31,31,31,31,31,11,16,17,25,17,7,39,39,47,39,47,39,19,39,31,19,0,7,7,39,39,39,39,39,39,39,31,39,8,1,1,2,39,47,31,31,3,31,31,39,29,24,17,30,31,39,39,12,23,5,5,5,21,18,31,19,39,47,31,19,39,39,39,31,31,7,31,39,31,39,39,39,31,39,39,31,31,7,7,39,39,3,31,31,31,39,31,0,2,28,1,1,27,46,1,2,7,7,0,42,32],\r\n\r\n  \"collision_map\":[0,6,0,0,0,0,0,8,4,4,0,0,2,0,0,1,0,1,0,14,0,0,12,0,0,3,0,0,0,0,0,0,0,0,0,8,0,0,3,0,1,0,0,11,0,0,0,8,0,4,2,0,0,0,9,4,5,5,5,4,2,0,14,0,1,0,14,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,11,0,0,0,0,0,9,1,0,0,1,1,0,1,1,1,1,1,0,0],\r\n\r\n  \"id\":\"03\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/06/zone04.json",
    "content": "{\r\n\r\n  \"doors\" : [\r\n\r\n    { \"x\":-16, \"y\":80, \"width\":16, \"height\":64,\r\n      \"destination_zone\":\"03\", \"destination_x\":192, \"destination_y\":-1 }\r\n\r\n  ],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"graphical_map\":[26,40,41,42,10,16,17,25,35,36,25,26,42,9,24,17,18,31,39,11,39,39,20,34,9,24,18,31,39,39,4,23,6,31,39,29,25,18,31,39,39,39,31,31,31,39,39,11,19,39,31,39,31,47,31,31,31,47,31,37,31,39,39,31,31,39,31,47,31,39,4,34,31,31,39,4,14,6,31,39,39,7,7,8,0,2,31,39,11,31,31,31,7,7,7,8,44,18,4,14,33,28,2,0,1,1,1,48],\r\n\r\n  \"collision_map\":[0,0,0,0,0,4,4,0,4,4,0,0,0,0,0,4,6,0,0,10,0,0,12,0,0,0,6,0,0,0,13,4,7,0,0,8,0,6,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,1,0,0,0,1,0,8,0,0,0,0,0,0,0,1,0,0,9,0,0,0,0,13,1,7,0,0,0,9,0,0,1,3,0,0,10,0,0,0,9,0,0,0,0,0,1,1,0,1,1,1,0,0,0,0],\r\n\r\n  \"id\":\"04\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/07/game-07.js",
    "content": "// 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 collideObject method out of Game.Door and into Game.Object.\r\n  3. Renamed collideObject to collideObjectCenter and made a new collideObject function for rectangular collision detection.\r\n  4. Added the Game.Carrot class and Game.Grass class.\r\n  5. Added frames for carrots and grass to the tile_set.\r\n  6. Made a slight change to the Game.Animator constructor.\r\n  7. Added carrot_count to count carrots.\r\n  8. Added the grass array to the zone file. Also reflected in Game.World\r\n\r\n*/\r\n\r\nconst Game = function() {\r\n\r\n  this.world    = new Game.World();\r\n\r\n  this.update   = function() {\r\n\r\n    this.world.update();\r\n\r\n  };\r\n\r\n};\r\nGame.prototype = { constructor : Game };\r\n\r\n// Made the default animation type \"loop\":\r\nGame.Animator = function(frame_set, delay, mode = \"loop\") {\r\n\r\n this.count       = 0;\r\n this.delay       = (delay >= 1) ? delay : 1;\r\n this.frame_set   = frame_set;\r\n this.frame_index = 0;\r\n this.frame_value = frame_set[0];\r\n this.mode        = mode;\r\n\r\n};\r\nGame.Animator.prototype = {\r\n\r\n constructor:Game.Animator,\r\n\r\n animate:function() {\r\n\r\n   switch(this.mode) {\r\n\r\n     case \"loop\" : this.loop(); break;\r\n     case \"pause\":              break;\r\n\r\n   }\r\n\r\n },\r\n\r\n changeFrameSet(frame_set, mode, delay = 10, frame_index = 0) {\r\n\r\n   if (this.frame_set === frame_set) { return; }\r\n\r\n   this.count       = 0;\r\n   this.delay       = delay;\r\n   this.frame_set   = frame_set;\r\n   this.frame_index = frame_index;\r\n   this.frame_value = frame_set[frame_index];\r\n   this.mode        = mode;\r\n\r\n },\r\n\r\n loop:function() {\r\n\r\n   this.count ++;\r\n\r\n   while(this.count > this.delay) {\r\n\r\n     this.count -= this.delay;\r\n\r\n     this.frame_index = (this.frame_index < this.frame_set.length - 1) ? this.frame_index + 1 : 0;\r\n\r\n     this.frame_value = this.frame_set[this.frame_index];\r\n\r\n   }\r\n\r\n }\r\n\r\n};\r\n\r\nGame.Collider = function() {\r\n\r\n  /* I changed this so all the checks happen in y first order. */\r\n  this.collide = function(value, object, tile_x, tile_y, tile_size) {\r\n\r\n    switch(value) {\r\n\r\n      case  1:     this.collidePlatformTop    (object, tile_y            ); break;\r\n      case  2:     this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  3: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  4:     this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  5: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  6: if (this.collidePlatformRight  (object, tile_x + tile_size)) return;\r\n                   this.collidePlatformBottom (object, tile_y + tile_size); break;\r\n      case  7: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case  8:     this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case  9: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 10: if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 11: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 12: if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 13: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n                   this.collidePlatformLeft   (object, tile_x            ); break;\r\n      case 14: if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n      case 15: if (this.collidePlatformTop    (object, tile_y            )) return;\r\n               if (this.collidePlatformBottom (object, tile_y + tile_size)) return;\r\n               if (this.collidePlatformLeft   (object, tile_x            )) return;\r\n                   this.collidePlatformRight  (object, tile_x + tile_size); break;\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\nGame.Collider.prototype = {\r\n\r\n  constructor: Game.Collider,\r\n\r\n  collidePlatformBottom:function(object, tile_bottom) {\r\n\r\n    if (object.getTop() < tile_bottom && object.getOldTop() >= tile_bottom) {\r\n\r\n      object.setTop(tile_bottom);\r\n      object.velocity_y = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformLeft:function(object, tile_left) {\r\n\r\n    if (object.getRight() > tile_left && object.getOldRight() <= tile_left) {\r\n\r\n      object.setRight(tile_left - 0.01);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformRight:function(object, tile_right) {\r\n\r\n    if (object.getLeft() < tile_right && object.getOldLeft() >= tile_right) {\r\n\r\n      object.setLeft(tile_right);\r\n      object.velocity_x = 0;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  },\r\n\r\n  collidePlatformTop:function(object, tile_top) {\r\n\r\n    if (object.getBottom() > tile_top && object.getOldBottom() <= tile_top) {\r\n\r\n      object.setBottom(tile_top - 0.01);\r\n      object.velocity_y = 0;\r\n      object.jumping    = false;\r\n      return true;\r\n\r\n    } return false;\r\n\r\n  }\r\n\r\n };\r\n\r\n// Added default values of 0 for offset_x and offset_y\r\nGame.Frame = function(x, y, width, height, offset_x = 0, offset_y = 0) {\r\n\r\n  this.x        = x;\r\n  this.y        = y;\r\n  this.width    = width;\r\n  this.height   = height;\r\n  this.offset_x = offset_x;\r\n  this.offset_y = offset_y;\r\n\r\n};\r\nGame.Frame.prototype = { constructor: Game.Frame };\r\n\r\nGame.Object = function(x, y, width, height) {\r\n\r\n this.height = height;\r\n this.width  = width;\r\n this.x      = x;\r\n this.y      = y;\r\n\r\n};\r\nGame.Object.prototype = {\r\n\r\n  constructor:Game.Object,\r\n\r\n  /* Now does rectangular collision detection. */\r\n  collideObject:function(object) {\r\n\r\n    if (this.getRight()  < object.getLeft()  ||\r\n        this.getBottom() < object.getTop()   ||\r\n        this.getLeft()   > object.getRight() ||\r\n        this.getTop()    > object.getBottom()) return false;\r\n\r\n    return true;\r\n\r\n  },\r\n\r\n  /* Does rectangular collision detection with the center of the object. */\r\n  collideObjectCenter:function(object) {\r\n\r\n    let center_x = object.getCenterX();\r\n    let center_y = object.getCenterY();\r\n\r\n    if (center_x < this.getLeft() || center_x > this.getRight() ||\r\n        center_y < this.getTop()  || center_y > this.getBottom()) return false;\r\n\r\n    return true;\r\n\r\n  },\r\n\r\n  getBottom : function()  { return this.y + this.height;       },\r\n  getCenterX: function()  { return this.x + this.width  * 0.5; },\r\n  getCenterY: function()  { return this.y + this.height * 0.5; },\r\n  getLeft   : function()  { return this.x;                     },\r\n  getRight  : function()  { return this.x + this.width;        },\r\n  getTop    : function()  { return this.y;                     },\r\n  setBottom : function(y) { this.y = y - this.height;          },\r\n  setCenterX: function(x) { this.x = x - this.width  * 0.5;    },\r\n  setCenterY: function(y) { this.y = y - this.height * 0.5;    },\r\n  setLeft   : function(x) { this.x = x;                        },\r\n  setRight  : function(x) { this.x = x - this.width;           },\r\n  setTop    : function(y) { this.y = y;                        }\r\n\r\n};\r\n\r\nGame.MovingObject = function(x, y, width, height, velocity_max = 15) {\r\n\r\n  Game.Object.call(this, x, y, width, height);\r\n\r\n  this.jumping      = false;\r\n  this.velocity_max = velocity_max;// added velocity_max so velocity can't go past 16\r\n  this.velocity_x   = 0;\r\n  this.velocity_y   = 0;\r\n  this.x_old        = x;\r\n  this.y_old        = y;\r\n\r\n};\r\n/* I added setCenterX, setCenterY, getCenterX, and getCenterY */\r\nGame.MovingObject.prototype = {\r\n\r\n  getOldBottom : function()  { return this.y_old + this.height;       },\r\n  getOldCenterX: function()  { return this.x_old + this.width  * 0.5; },\r\n  getOldCenterY: function()  { return this.y_old + this.height * 0.5; },\r\n  getOldLeft   : function()  { return this.x_old;                     },\r\n  getOldRight  : function()  { return this.x_old + this.width;        },\r\n  getOldTop    : function()  { return this.y_old;                     },\r\n  setOldBottom : function(y) { this.y_old = y    - this.height;       },\r\n  setOldCenterX: function(x) { this.x_old = x    - this.width  * 0.5; },\r\n  setOldCenterY: function(y) { this.y_old = y    - this.height * 0.5; },\r\n  setOldLeft   : function(x) { this.x_old = x;                        },\r\n  setOldRight  : function(x) { this.x_old = x    - this.width;        },\r\n  setOldTop    : function(y) { this.y_old = y;                        }\r\n\r\n};\r\nObject.assign(Game.MovingObject.prototype, Game.Object.prototype);\r\nGame.MovingObject.prototype.constructor = Game.MovingObject;\r\n\r\n/* The carrot class extends Game.Object and Game.Animation. */\r\nGame.Carrot = function(x, y) {\r\n\r\n  Game.Object.call(this, x, y, 7, 14);\r\n  Game.Animator.call(this, Game.Carrot.prototype.frame_sets[\"twirl\"], 15);\r\n\r\n  this.frame_index = Math.floor(Math.random() * 2);\r\n\r\n  /* base_x and base_y are the point around which the carrot revolves. position_x\r\n  and y are used to track the vector facing away from the base point to give the carrot\r\n  the floating effect. */\r\n  this.base_x     = x;\r\n  this.base_y     = y;\r\n  this.position_x = Math.random() * Math.PI * 2;\r\n  this.position_y = this.position_x * 2;\r\n\r\n};\r\nGame.Carrot.prototype = {\r\n\r\n  frame_sets: { \"twirl\":[12, 13] },\r\n\r\n  updatePosition:function() {\r\n\r\n    this.position_x += 0.1;\r\n    this.position_y += 0.2;\r\n\r\n    this.x = this.base_x + Math.cos(this.position_x) * 2;\r\n    this.y = this.base_y + Math.sin(this.position_y);\r\n\r\n  }\r\n\r\n};\r\nObject.assign(Game.Carrot.prototype, Game.Animator.prototype);\r\nObject.assign(Game.Carrot.prototype, Game.Object.prototype);\r\nGame.Carrot.prototype.constructor = Game.Carrot;\r\n\r\nGame.Grass = function(x, y) {\r\n\r\n  Game.Animator.call(this, Game.Grass.prototype.frame_sets[\"wave\"], 25);\r\n\r\n  this.x = x;\r\n  this.y = y;\r\n\r\n};\r\nGame.Grass.prototype = {\r\n\r\n  frame_sets: {\r\n\r\n    \"wave\":[14, 15, 16, 15]\r\n\r\n  }\r\n\r\n};\r\nObject.assign(Game.Grass.prototype, Game.Animator.prototype);\r\n\r\nGame.Door = function(door) {\r\n\r\n Game.Object.call(this, door.x, door.y, door.width, door.height);\r\n\r\n this.destination_x    = door.destination_x;\r\n this.destination_y    = door.destination_y;\r\n this.destination_zone = door.destination_zone;\r\n\r\n};\r\nGame.Door.prototype = {};\r\nObject.assign(Game.Door.prototype, Game.Object.prototype);\r\nGame.Door.prototype.constructor = Game.Door;\r\n\r\nGame.Player = function(x, y) {\r\n\r\n  Game.MovingObject.call(this, x, y, 7, 12);\r\n\r\n  Game.Animator.call(this, Game.Player.prototype.frame_sets[\"idle-left\"], 10);\r\n\r\n  this.jumping     = true;\r\n  this.direction_x = -1;\r\n  this.velocity_x  = 0;\r\n  this.velocity_y  = 0;\r\n\r\n};\r\nGame.Player.prototype = {\r\n\r\n  frame_sets: {\r\n\r\n    \"idle-left\" : [0],\r\n    \"jump-left\" : [1],\r\n    \"move-left\" : [2, 3, 4, 5],\r\n    \"idle-right\": [6],\r\n    \"jump-right\": [7],\r\n    \"move-right\": [8, 9, 10, 11]\r\n\r\n  },\r\n\r\n  jump: function() {\r\n\r\n    /* Made it so you can only jump if you aren't falling faster than 10px per frame. */\r\n    if (!this.jumping && this.velocity_y < 10) {\r\n\r\n      this.jumping     = true;\r\n      this.velocity_y -= 13;\r\n\r\n    }\r\n\r\n  },\r\n\r\n  moveLeft: function() {\r\n\r\n    this.direction_x = -1;\r\n    this.velocity_x -= 0.55;\r\n\r\n  },\r\n\r\n  moveRight:function(frame_set) {\r\n\r\n    this.direction_x = 1;\r\n    this.velocity_x += 0.55;\r\n\r\n  },\r\n\r\n  updateAnimation:function() {\r\n\r\n    if (this.velocity_y < 0) {\r\n\r\n      if (this.direction_x < 0) this.changeFrameSet(this.frame_sets[\"jump-left\"], \"pause\");\r\n      else this.changeFrameSet(this.frame_sets[\"jump-right\"], \"pause\");\r\n\r\n    } else if (this.direction_x < 0) {\r\n\r\n      if (this.velocity_x < -0.1) this.changeFrameSet(this.frame_sets[\"move-left\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-left\"], \"pause\");\r\n\r\n    } else if (this.direction_x > 0) {\r\n\r\n      if (this.velocity_x > 0.1) this.changeFrameSet(this.frame_sets[\"move-right\"], \"loop\", 5);\r\n      else this.changeFrameSet(this.frame_sets[\"idle-right\"], \"pause\");\r\n\r\n    }\r\n\r\n    this.animate();\r\n\r\n  },\r\n\r\n  updatePosition:function(gravity, friction) {\r\n\r\n    this.x_old = this.x;\r\n    this.y_old = this.y;\r\n\r\n    this.velocity_y += gravity;\r\n    this.velocity_x *= friction;\r\n\r\n    /* Made it so that velocity cannot exceed velocity_max */\r\n    if (Math.abs(this.velocity_x) > this.velocity_max)\r\n    this.velocity_x = this.velocity_max * Math.sign(this.velocity_x);\r\n\r\n    if (Math.abs(this.velocity_y) > this.velocity_max)\r\n    this.velocity_y = this.velocity_max * Math.sign(this.velocity_y);\r\n\r\n    this.x    += this.velocity_x;\r\n    this.y    += this.velocity_y;\r\n\r\n  }\r\n\r\n};\r\nObject.assign(Game.Player.prototype, Game.MovingObject.prototype);\r\nObject.assign(Game.Player.prototype, Game.Animator.prototype);\r\nGame.Player.prototype.constructor = Game.Player;\r\n\r\nGame.TileSet = function(columns, tile_size) {\r\n\r\n  this.columns    = columns;\r\n  this.tile_size  = tile_size;\r\n\r\n  let f = Game.Frame;\r\n\r\n  this.frames = [new f(115,  96, 13, 16, 0, -4), // idle-left\r\n                 new f( 50,  96, 13, 16, 0, -4), // jump-left\r\n                 new f(102,  96, 13, 16, 0, -4), new f(89, 96, 13, 16, 0, -4), new f(76, 96, 13, 16, 0, -4), new f(63, 96, 13, 16, 0, -4), // walk-left\r\n                 new f(  0, 112, 13, 16, 0, -4), // idle-right\r\n                 new f( 65, 112, 13, 16, 0, -4), // jump-right\r\n                 new f( 13, 112, 13, 16, 0, -4), new f(26, 112, 13, 16, 0, -4), new f(39, 112, 13, 16, 0, -4), new f(52, 112, 13, 16, 0, -4), // walk-right\r\n                 new f( 81, 112, 14, 16), new f(96, 112, 16, 16), // carrot\r\n                 new f(112, 115, 16,  4), new f(112, 124, 16, 4), new f(112, 119, 16, 4) // grass\r\n                ];\r\n\r\n};\r\nGame.TileSet.prototype = { constructor: Game.TileSet };\r\n\r\nGame.World = function(friction = 0.85, gravity = 2) {\r\n\r\n  this.collider     = new Game.Collider();\r\n\r\n  this.friction     = friction;\r\n  this.gravity      = gravity;\r\n\r\n  this.columns      = 12;\r\n  this.rows         = 9;\r\n\r\n  this.tile_set     = new Game.TileSet(8, 16);\r\n  this.player       = new Game.Player(32, 76);\r\n\r\n  this.zone_id      = \"00\";\r\n\r\n  this.carrots      = [];// the array of carrots in this zone;\r\n  this.carrot_count = 0;// the number of carrots you have.\r\n  this.doors        = [];\r\n  this.door         = undefined;\r\n\r\n  this.height       = this.tile_set.tile_size * this.rows;\r\n  this.width        = this.tile_set.tile_size * this.columns;\r\n\r\n};\r\nGame.World.prototype = {\r\n\r\n  constructor: Game.World,\r\n\r\n  collideObject:function(object) {\r\n\r\n    /* I got rid of the world boundary collision. Now it's up to the tiles to keep\r\n    the player from falling out of the world. */\r\n\r\n    var bottom, left, right, top, value;\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    top    = Math.floor(object.getTop()    / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[top * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, top * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    left   = Math.floor(object.getLeft()   / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + left];\r\n    this.collider.collide(value, object, left * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n    bottom = Math.floor(object.getBottom() / this.tile_set.tile_size);\r\n    right  = Math.floor(object.getRight()  / this.tile_set.tile_size);\r\n    value  = this.collision_map[bottom * this.columns + right];\r\n    this.collider.collide(value, object, right * this.tile_set.tile_size, bottom * this.tile_set.tile_size, this.tile_set.tile_size);\r\n\r\n  },\r\n\r\n  setup:function(zone) {\r\n\r\n    this.carrots            = new Array();\r\n    this.doors              = new Array();\r\n    this.grass              = new Array();\r\n    this.collision_map      = zone.collision_map;\r\n    this.graphical_map      = zone.graphical_map;\r\n    this.columns            = zone.columns;\r\n    this.rows               = zone.rows;\r\n    this.zone_id            = zone.id;\r\n\r\n    for (let index = zone.carrots.length - 1; index > -1; -- index) {\r\n\r\n      let carrot = zone.carrots[index];\r\n      this.carrots[index] = new Game.Carrot(carrot[0] * this.tile_set.tile_size + 5, carrot[1] * this.tile_set.tile_size - 2);\r\n\r\n    }\r\n\r\n    for (let index = zone.doors.length - 1; index > -1; -- index) {\r\n\r\n      let door = zone.doors[index];\r\n      this.doors[index] = new Game.Door(door);\r\n\r\n    }\r\n\r\n    for (let index = zone.grass.length - 1; index > -1; -- index) {\r\n\r\n      let grass = zone.grass[index];\r\n      this.grass[index] = new Game.Grass(grass[0] * this.tile_set.tile_size, grass[1] * this.tile_set.tile_size + 12);\r\n\r\n    }\r\n\r\n    if (this.door) {\r\n\r\n      if (this.door.destination_x != -1) {\r\n\r\n        this.player.setCenterX   (this.door.destination_x);\r\n        this.player.setOldCenterX(this.door.destination_x);// It's important to reset the old position as well.\r\n\r\n      }\r\n\r\n      if (this.door.destination_y != -1) {\r\n\r\n        this.player.setCenterY   (this.door.destination_y);\r\n        this.player.setOldCenterY(this.door.destination_y);\r\n\r\n      }\r\n\r\n      this.door = undefined;// Make sure to reset this.door so we don't trigger a zone load.\r\n\r\n    }\r\n\r\n  },\r\n\r\n  update:function() {\r\n\r\n    this.player.updatePosition(this.gravity, this.friction);\r\n\r\n    this.collideObject(this.player);\r\n\r\n    for (let index = this.carrots.length - 1; index > -1; -- index) {\r\n\r\n      let carrot = this.carrots[index];\r\n\r\n      carrot.updatePosition();\r\n      carrot.animate();\r\n\r\n      if (carrot.collideObject(this.player)) {\r\n\r\n        this.carrots.splice(this.carrots.indexOf(carrot), 1);\r\n        this.carrot_count ++;\r\n\r\n      }\r\n\r\n    }\r\n\r\n    for(let index = this.doors.length - 1; index > -1; -- index) {\r\n\r\n      let door = this.doors[index];\r\n\r\n      if (door.collideObjectCenter(this.player)) {\r\n\r\n        this.door = door;\r\n\r\n      };\r\n\r\n    }\r\n\r\n    for (let index = this.grass.length - 1; index > -1; -- index) {\r\n\r\n      let grass = this.grass[index];\r\n\r\n      grass.animate();\r\n\r\n    }\r\n\r\n    this.player.updateAnimation();\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "content/rabbit-trap/07/main-07.js",
    "content": "// 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  2. Added a p element for showing the number of carrots collected.\r\n\r\n*/\r\n\r\nwindow.addEventListener(\"load\", function(event) {\r\n\r\n  \"use strict\";\r\n\r\n  //// CONSTANTS ////\r\n\r\n  const ZONE_PREFIX = \"07/zone\";\r\n  const ZONE_SUFFIX = \".json\";\r\n\r\n      /////////////////\r\n    //// CLASSES ////\r\n  /////////////////\r\n\r\n  const AssetsManager = function() {\r\n\r\n    this.tile_set_image = undefined;\r\n\r\n  };\r\n\r\n  AssetsManager.prototype = {\r\n\r\n    constructor: Game.AssetsManager,\r\n\r\n    requestJSON:function(url, callback) {\r\n\r\n      let request = new XMLHttpRequest();\r\n\r\n      request.addEventListener(\"load\", function(event) {\r\n\r\n        callback(JSON.parse(this.responseText));\r\n\r\n      }, { once:true });\r\n\r\n      request.open(\"GET\", url);\r\n      request.send();\r\n\r\n    },\r\n\r\n    requestImage:function(url, callback) {\r\n\r\n      let image = new Image();\r\n\r\n      image.addEventListener(\"load\", function(event) {\r\n\r\n        callback(image);\r\n\r\n      }, { once:true });\r\n\r\n      image.src = url;\r\n\r\n    },\r\n\r\n  };\r\n\r\n      ///////////////////\r\n    //// FUNCTIONS ////\r\n  ///////////////////\r\n\r\n  var keyDownUp = function(event) {\r\n\r\n    controller.keyDownUp(event.type, event.keyCode);\r\n\r\n  };\r\n\r\n  var resize = function(event) {\r\n\r\n    display.resize(document.documentElement.clientWidth, document.documentElement.clientHeight, game.world.height / game.world.width);\r\n    display.render();\r\n\r\n    var rectangle = display.context.canvas.getBoundingClientRect();\r\n\r\n    p.style.left = rectangle.left + \"px\";\r\n    p.style.top  = rectangle.top + \"px\";\r\n    p.style.fontSize = game.world.tile_set.tile_size * rectangle.height / game.world.height + \"px\";\r\n\r\n  };\r\n\r\n  var render = function() {\r\n\r\n    var frame = undefined;\r\n\r\n    display.drawMap   (assets_manager.tile_set_image,\r\n    game.world.tile_set.columns, game.world.graphical_map, game.world.columns,  game.world.tile_set.tile_size);\r\n\r\n    for (let index = game.world.carrots.length - 1; index > -1; -- index) {\r\n\r\n      let carrot = game.world.carrots[index];\r\n\r\n      frame = game.world.tile_set.frames[carrot.frame_value];\r\n\r\n      display.drawObject(assets_manager.tile_set_image,\r\n      frame.x, frame.y,\r\n      carrot.x + Math.floor(carrot.width * 0.5 - frame.width * 0.5) + frame.offset_x,\r\n      carrot.y + frame.offset_y, frame.width, frame.height);\r\n\r\n    }\r\n\r\n    frame = game.world.tile_set.frames[game.world.player.frame_value];\r\n\r\n    display.drawObject(assets_manager.tile_set_image,\r\n    frame.x, frame.y,\r\n    game.world.player.x + Math.floor(game.world.player.width * 0.5 - frame.width * 0.5) + frame.offset_x,\r\n    game.world.player.y + frame.offset_y, frame.width, frame.height);\r\n\r\n    for (let index = game.world.grass.length - 1; index > -1; -- index) {\r\n\r\n      let grass = game.world.grass[index];\r\n\r\n      frame = game.world.tile_set.frames[grass.frame_value];\r\n\r\n      display.drawObject(assets_manager.tile_set_image,\r\n      frame.x, frame.y,\r\n      grass.x + frame.offset_x,\r\n      grass.y + frame.offset_y, frame.width, frame.height);\r\n\r\n    }\r\n\r\n    p.innerHTML = \"Carrots: \" + game.world.carrot_count;\r\n\r\n    display.render();\r\n\r\n  };\r\n\r\n  var update = function() {\r\n\r\n    if (controller.left.active ) { game.world.player.moveLeft ();                               }\r\n    if (controller.right.active) { game.world.player.moveRight();                               }\r\n    if (controller.up.active   ) { game.world.player.jump();      controller.up.active = false; }\r\n\r\n    game.update();\r\n\r\n    if (game.world.door) {\r\n\r\n      engine.stop();\r\n\r\n      assets_manager.requestJSON(ZONE_PREFIX + game.world.door.destination_zone + ZONE_SUFFIX, (zone) => {\r\n\r\n        game.world.setup(zone);\r\n\r\n        engine.start();\r\n\r\n      });\r\n\r\n      return;\r\n\r\n    }\r\n\r\n  };\r\n\r\n      /////////////////\r\n    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var assets_manager = new AssetsManager();\r\n  var controller     = new Controller();\r\n  var display        = new Display(document.querySelector(\"canvas\"));\r\n  var game           = new Game();\r\n  var engine         = new Engine(1000/30, render, update);\r\n\r\n  var p              = document.createElement(\"p\");\r\n  p.setAttribute(\"style\", \"color:#c07000; font-size:2.0em; position:fixed;\");\r\n  p.innerHTML = \"Carrots: 0\";\r\n  document.body.appendChild(p);\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.buffer.canvas.height = game.world.height;\r\n  display.buffer.canvas.width  = game.world.width;\r\n  display.buffer.imageSmoothingEnabled = false;\r\n\r\n  assets_manager.requestJSON(ZONE_PREFIX + game.world.zone_id + ZONE_SUFFIX, (zone) => {\r\n\r\n    game.world.setup(zone);\r\n\r\n    assets_manager.requestImage(\"rabbit-trap.png\", (image) => {\r\n\r\n      assets_manager.tile_set_image = image;\r\n\r\n      resize();\r\n      engine.start();\r\n\r\n    });\r\n\r\n  });\r\n\r\n  window.addEventListener(\"keydown\", keyDownUp);\r\n  window.addEventListener(\"keyup\"  , keyDownUp);\r\n  window.addEventListener(\"resize\" , resize);\r\n\r\n});\r\n"
  },
  {
    "path": "content/rabbit-trap/07/zone00.json",
    "content": "{\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], [3, 7], [5, 7], [7, 7], [9, 7], [10, 7]],\r\n\r\n  \"doors\":[],\r\n\r\n  \"columns\":12,\r\n  \"rows\"   :9,\r\n\r\n  \"collision_map\":[0,4,4,4,4,0,4,4,0,4,4,0,2,0,0,0,0,10,0,0,14,0,0,8,2,0,0,0,0,10,0,0,0,0,0,8,0,3,0,0,13,4,7,0,0,0,13,0,0,6,0,0,0,0,0,0,0,0,0,8,2,0,0,1,0,0,0,0,11,0,0,8,2,0,0,0,0,0,11,0,10,0,13,0,0,3,0,0,11,0,10,0,10,0,0,8,0,0,1,1,0,1,0,1,0,1,1,0],\r\n  \"graphical_map\":[27,36,35,5,36,25,17,35,44,17,35,5,30,39,39,39,39,11,31,39,19,31,39,7,38,31,39,31,31,11,39,39,39,39,39,7,30,3,31,39,4,23,6,31,31,39,4,14,22,21,39,31,31,39,31,39,31,39,31,20,38,39,39,47,39,31,39,39,3,39,31,12,10,31,31,31,31,39,3,39,11,31,4,46,40,2,39,39,3,39,11,39,11,31,39,8,24,49,2,3,37,27,23,13,11,12,13,8],\r\n\r\n  \"id\":\"00\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/rabbit-trap.css",
    "content": "/* 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\nbody {\r\n\r\n  align-content:center;\r\n  align-items:space-around;\r\n  background-color:#202830;\r\n  display:grid;\r\n  justify-items:center;\r\n  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\n/* 04/07/2018 */\r\n\r\ncanvas { image-rendering:pixelated; }\r\n\r\n#menu {\r\n\r\n  background-color:rgba(0, 0, 0, 0.5);\r\n  color:#ffffff;\r\n  cursor:pointer;\r\n  display:grid;\r\n  max-height:100%;\r\n  overflow-y:auto;\r\n  padding:8px;\r\n  position:fixed;\r\n  right:0px;\r\n  top:0px;\r\n  user-select:none;\r\n\r\n  -webkit-tap-highlight-color:transparent;\r\n\r\n}\r\n\r\n#menu p {\r\n\r\n  color:#ffffff;\r\n  font-size:2.0em;\r\n\r\n}\r\n\r\n#menu-list {\r\n\r\n  display:none;\r\n\r\n}\r\n\r\n#menu-list a {\r\n\r\n  color:#ffffff;\r\n  font-size:1.5em;\r\n  text-decoration:none;\r\n\r\n}\r\n"
  },
  {
    "path": "content/rabbit-trap/rabbit-trap.html",
    "content": "<!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\" content = \"user-scalable=no,width=device-width\">\r\n    <link href = \"rabbit-trap.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    <title>Rabbit Trap</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <!-- Added a menu to navigate projects from the main page -->\r\n    <div id = \"menu\">\r\n      <p>menu</p>\r\n      <div id = \"menu-list\">\r\n        <br>\r\n        <a href = \"rabbit-trap.html?01\">part1</a>\r\n        <a href = \"rabbit-trap.html?02\">part2</a>\r\n        <a href = \"rabbit-trap.html?03\">part3</a>\r\n        <a href = \"rabbit-trap.html?04\">part4</a>\r\n        <a href = \"rabbit-trap.html?05\">part5</a>\r\n        <a href = \"rabbit-trap.html?06\">part6</a>\r\n        <a href = \"rabbit-trap.html?07\">part7</a>\r\n      </div>\r\n    </div>\r\n\r\n    <canvas></canvas>\r\n\r\n    <!-- Since Rabbit Trap is a multi-part series and I didn't feel like writing\r\n    html and css for every single part, I decided to dynamically add the appropriate\r\n    js file containing the game logic for each part based on url parameters. -->\r\n    <script type = \"text/javascript\">\r\n\r\n      let part = String(window.location).split(\"?\")[1];\r\n\r\n      /* Added on 03/09/2018 to allow reusing scripts from previous parts. */\r\n      let parts = {\r\n\r\n        \"01\":[\"01/controller-01.js\", \"01/display-01.js\", \"01/engine-01.js\", \"01/game-01.js\", \"01/main-01.js\"],\r\n        \"02\":[\"02/controller-02.js\", \"02/display-02.js\", \"01/engine-01.js\", \"02/game-02.js\", \"02/main-02.js\"],\r\n        \"03\":[\"02/controller-02.js\", \"03/display-03.js\", \"01/engine-01.js\", \"03/game-03.js\", \"03/main-03.js\"],\r\n        \"04\":[\"02/controller-02.js\", \"04/display-04.js\", \"01/engine-01.js\", \"04/game-04.js\", \"03/main-03.js\"],\r\n        \"05\":[\"02/controller-02.js\", \"05/display-05.js\", \"01/engine-01.js\", \"05/game-05.js\", \"05/main-05.js\"],\r\n        \"06\":[\"02/controller-02.js\", \"05/display-05.js\", \"06/engine-06.js\", \"06/game-06.js\", \"06/main-06.js\"],\r\n        \"07\":[\"02/controller-02.js\", \"05/display-05.js\", \"06/engine-06.js\", \"07/game-07.js\", \"07/main-07.js\"],\r\n\r\n      };\r\n\r\n      switch(part) {\r\n\r\n        case \"01\": case \"02\": case \"03\": case \"04\": case \"05\": case \"06\": case \"07\": break;\r\n        default:\r\n          part = \"07\";\r\n\r\n      }\r\n\r\n      for (let index = 0; index < parts[part].length; index ++) {\r\n\r\n        let script = document.createElement(\"script\");\r\n        script.setAttribute(\"type\", \"text/javascript\");\r\n        script.setAttribute(\"src\", parts[part][index]);\r\n        document.head.appendChild(script);\r\n\r\n      }\r\n\r\n      let menu      = document.getElementById(\"menu\");\r\n      let menu_list = document.getElementById(\"menu-list\");\r\n\r\n      menu.addEventListener(\"click\", function(event) {\r\n\r\n        menu_list.style.display = (menu_list.style.display == \"none\") ? \"grid\" : \"none\";\r\n\r\n      });\r\n\r\n      menu_list.style.display = \"none\";\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/rectangle-collision/rectangle-collision.html",
    "content": "<!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      * { margin:0; padding:0; overflow:hidden; }\r\n\r\n      html, body { height:100%; width:100%; }\r\n\r\n      body { background-color:#ffffff; display:grid; }\r\n\r\n      canvas { align-self:center; justify-self:center; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Rectangle {\r\n\r\n        constructor (x, y, w, h) {\r\n\r\n          this.x = x; this.y = y; this.w = w; this.h = h;\r\n\r\n        }\r\n\r\n        get cx() { return this.x + this.w * 0.5; }\r\n        get cy() { return this.y + this.h * 0.5; }\r\n\r\n        collideRectangle(rect) {\r\n\r\n          var dx = rect.cx - this.cx;// x difference between centers\r\n          var dy = rect.cy - this.cy;// y difference between centers\r\n          var aw = (rect.w + this.w) * 0.5;// average width\r\n          var ah = (rect.h + this.h) * 0.5;// average height\r\n\r\n          /* If either distance is greater than the average dimension there is no collision. */\r\n          if (Math.abs(dx) > aw || Math.abs(dy) > ah) return false;\r\n\r\n          /* To determine which region of this rectangle the rect's center\r\n          point is in, we have to account for the scale of the this rectangle.\r\n          To do that, we divide dx and dy by it's width and height respectively. */\r\n          if (Math.abs(dx / this.w) > Math.abs(dy / this.h)) {\r\n\r\n            if (dx < 0) rect.x = this.x - rect.w;// left\r\n            else rect.x = this.x + this.w; // right\r\n\r\n          } else {\r\n\r\n            if (dy < 0) rect.y = this.y - rect.h; // top\r\n            else rect.y = this.y + this.h; // bottom\r\n\r\n          }\r\n\r\n          return true;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var p = document.querySelector(\"p\");\r\n\r\n      var screen_h = document.documentElement.clientHeight;\r\n      var screen_w = document.documentElement.clientWidth;\r\n\r\n      var r1 = new Rectangle(100, 100, Math.round(Math.random() * 100 + 50), Math.round(Math.random() * 100 + 50));\r\n      var r2 = new Rectangle(100, 100, Math.round(Math.random() * 100 + 50), Math.round(Math.random() * 100 + 50));\r\n\r\n      var pointer = { x:0, y:0 };\r\n\r\n      function loop() {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        screen_h = document.documentElement.clientHeight - 16;\r\n        screen_w = document.documentElement.clientWidth - 16;\r\n\r\n        context.canvas.height = screen_h;\r\n        context.canvas.width = screen_w;\r\n\r\n        context.fillStyle = \"#202830\";\r\n        context.fillRect(0, 0, screen_w, screen_h);\r\n\r\n        r1.x = screen_w * 0.5 - r1.w * 0.5;\r\n        r1.y = screen_h * 0.5 - r1.h * 0.5;\r\n\r\n        r2.x = pointer.x - r2.w * 0.5;\r\n        r2.y = pointer.y - r2.h * 0.5;\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.fillRect(r1.x, r1.y, r1.w, r1.h);\r\n\r\n        var collision = false;\r\n        if (r1.collideRectangle(r2)) collision = true;\r\n\r\n        context.fillStyle = \"#ff0000\";\r\n        context.fillRect(r2.x, r2.y , r2.w, r2.h);\r\n\r\n        if (collision) {\r\n\r\n          context.strokeStyle = \"#800000\";\r\n          context.rect(pointer.x - r2.w * 0.5, pointer.y - r2.h * 0.5, r2.w, r2.h);\r\n          context.stroke();\r\n\r\n          context.strokeStyle = \"#800000\";\r\n          context.moveTo(r1.cx, r1.cy);\r\n          context.lineTo(pointer.x, pointer.y);\r\n          context.stroke();\r\n\r\n          context.strokeStyle = \"#808080\";\r\n          context.beginPath();\r\n          context.moveTo(r1.x, r1.y);\r\n          context.lineTo(r1.x + r1.w, r1.y + r1.h);\r\n          context.stroke();\r\n          context.moveTo(r1.x + r1.w, r1.y);\r\n          context.lineTo(r1.x, r1.y + r1.h);\r\n          context.stroke();\r\n\r\n        }\r\n\r\n      }\r\n\r\n      loop();\r\n\r\n      window.addEventListener(\"mousemove\", function(event) {\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - rect.left;\r\n        pointer.y = event.clientY - rect.top;\r\n\r\n      });\r\n\r\n      var offset_x = offset_y = 0;\r\n\r\n      window.addEventListener(\"touchstart\", function(event) {\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        offset_x = event.targetTouches[0].clientX - rect.left - r2.cx;\r\n        offset_y = event.targetTouches[0].clientY - rect.top - r2.cy;\r\n\r\n      });\r\n\r\n      window.addEventListener(\"touchmove\", function(event) {\r\n\r\n        var rect = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.targetTouches[0].clientX - rect.left - offset_x;\r\n        pointer.y = event.targetTouches[0].clientY - rect.top - offset_y;\r\n\r\n      });\r\n\r\n      window.addEventListener(\"click\", function(event) {\r\n\r\n        r1.h = Math.round(Math.random() * 100 + 50);\r\n        r1.w = Math.round(Math.random() * 100 + 50);\r\n        r2.h = Math.round(Math.random() * 100 + 50);\r\n        r2.w = Math.round(Math.random() * 100 + 50);\r\n\r\n      });\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/shoot/shoot.html",
    "content": "<!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</title>\r\n\r\n\t\t<style>\r\n\r\n\t\t\t* { margin:0; overflow:hidden; padding:0; }\r\n\r\n\t\t\thtml { height:100%; width:100%; }\r\n\r\n\t\t\tbody { background-color:#000000; display:grid; height:100%; width:100%; }\r\n\r\n\t\t\tcanvas { align-self:center; justify-self:center; }\r\n\r\n\t\t</style>\r\n\r\n\t</head>\r\n\r\n\t<body>\r\n\r\n\t\t<canvas></canvas>\r\n\r\n\t\t<script type = \"text/javascript\">\r\n\r\n\t\t\tconst Bullet = function(x, y, d, vx) {\r\n\r\n\t\t\t\tthis.x = x; this.y = y; this.d = d; this.vx = 5 * d + vx;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tBullet.prototype = {\r\n\r\n\t\t\t\tupdatePosition:function() {\r\n\r\n\t\t\t\t\tthis.x += this.vx;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t};\r\n\r\n\t\t\tconst Player = function(x, y, d) {\r\n\r\n\t\t\t\tthis.x = x; this.y = y; this.d = d;\r\n\t\t\t\tthis.source_x = 64;\r\n\t\t\t\tthis.jumping = true;\r\n\t\t\t\tthis.vx = 0; this.vy = 0;\r\n\r\n\t\t\t};\r\n\r\n\t\t\tPlayer.prototype = {\r\n\r\n\t\t\t\tjump:function() {\r\n\r\n\t\t\t\t\tif (!this.jumping) {\r\n\r\n\t\t\t\t\t\tthis.jumping = true;\r\n\t\t\t\t\t\tthis.vy = -20;\r\n\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t},\r\n\r\n\t\t\t\tupdatePosition:function(x, y) {\r\n\r\n\t\t\t\t\tthis.vx = (x - this.x) * 0.01;\r\n\r\n\t\t\t\t\tthis.x += this.vx;\r\n\t\t\t\t\tthis.y += this.vy;\r\n\r\n\t\t\t\t\tif (this.vx < 0) { this.d = -1; this.source_x = 64; }\r\n\t\t\t\t\telse { this.d = 1; this.source_x = 80; }\r\n\r\n\t\t\t\t\tthis.vx *= 0.9;\r\n\t\t\t\t\tthis.vy *= 0.9;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t};\r\n\r\n\t\t\tvar tile_sheet = new Image();\r\n\r\n\t\t\tvar columns = 16; var rows = 16; var tile_size = 16; var scale = 1;\r\n\t\t\tvar map = [1,1,0,0,1,1,0,0,0,1,0,1,1,1,0,1,\r\n\t\t\t\t\t\t\t   0,1,0,0,0,1,0,0,1,1,0,1,0,1,1,0,\r\n\t\t\t\t\t\t\t   0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,\r\n\t\t\t\t\t\t\t   1,0,1,0,0,0,0,1,1,0,1,0,0,0,0,0,\r\n\t\t\t\t\t\t\t   0,0,1,1,1,0,0,1,0,0,1,0,0,1,0,0,\r\n\t\t\t\t\t\t\t   1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,\r\n\t\t\t\t\t\t\t   1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,\r\n\t\t\t\t\t\t\t   0,1,1,0,0,0,1,0,0,0,0,0,1,1,0,0,\r\n\t\t\t\t\t\t\t   0,0,0,1,1,0,0,0,0,0,1,0,1,0,1,0,\r\n\t\t\t\t\t\t\t   1,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,\r\n\t\t\t\t\t\t\t   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n\t\t\t\t\t\t\t   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,\r\n\t\t\t\t\t\t\t   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,\r\n\t\t\t\t\t\t\t   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,\r\n\t\t\t\t\t\t\t   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,\r\n\t\t\t\t\t\t\t   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3];\r\n\r\n\t\t\tvar context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\t\t\tvar buffer  = document.createElement(\"canvas\").getContext(\"2d\");\r\n\r\n\t\t\tvar height = document.documentElement.clientHeight - 16;\r\n\t\t\tvar width  = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\tvar pointer = { down:false, x:0, y:0 }\r\n\r\n\t\t\tvar player = new Player (100, 100, -1);\r\n\r\n\t\t\tvar bullets = new Array();\r\n\r\n\t\t\tfunction loop() {\r\n\r\n\t\t\t\twindow.requestAnimationFrame(loop);\r\n\r\n\t\t\t\theight = document.documentElement.clientHeight - 16;\r\n\t\t\t\twidth  = document.documentElement.clientWidth - 16;\r\n\r\n\t\t\t\tvar min_size = height < width ? height : width;\r\n\r\n\t\t\t\tcontext.canvas.height = min_size;\r\n\t\t\t\tcontext.canvas.width  = min_size;\r\n\t\t\t\t// Added this after the video was made\r\n\t\t\t\tcontext.imageSmoothingEnabled = false;\r\n\r\n\t\t\t\tscale = min_size / buffer.canvas.width;\r\n\r\n\t\t\t\tfor (let index = map.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet value = map[index];\r\n\t\t\t\t\tlet tile_x = (index % columns) * tile_size;\r\n\t\t\t\t\tlet tile_y = Math.floor(index / columns) * tile_size;\r\n\r\n\t\t\t\t\tbuffer.drawImage(tile_sheet, value * tile_size, 0, tile_size, tile_size, tile_x, tile_y, tile_size, tile_size);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (pointer.down) {\r\n\r\n\t\t\t\t\tpointer.down = false;\r\n\t\t\t\t\tif (pointer.y < player.y)\tplayer.jump();\r\n\r\n\t\t\t\t\tbullets.push(new Bullet(player.x + tile_size * 0.5 - 2 + player.d * 4, player.y + 8, player.d, player.vx));\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tplayer.vy += 1;\r\n\t\t\t\tplayer.updatePosition(pointer.x, pointer.y);\r\n\r\n\t\t\t\tif (player.y + tile_size > tile_size * 11 + 4) { player.y = tile_size * 10 + 4; player.jumping = false; player.vy = 0; }\r\n\r\n\t\t\t\tbuffer.drawImage(tile_sheet, player.source_x, 0, tile_size, tile_size, player.x, player.y, tile_size, tile_size);\r\n\r\n\t\t\t\tfor (let index = bullets.length - 1; index > -1; -- index) {\r\n\r\n\t\t\t\t\tlet bullet = bullets[index];\r\n\r\n\t\t\t\t\tbullet.updatePosition();\r\n\r\n\t\t\t\t\tbuffer.fillStyle = \"#000000\";\r\n\t\t\t\t\tbuffer.fillRect(bullet.x, bullet.y, 4, 4);\r\n\r\n\t\t\t\t\tif (bullet.x < -4 || bullet.x > buffer.canvas.width) bullets.splice(index, 1);\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcontext.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n\t\t\t}\r\n\r\n\t\t\tbuffer.canvas.height = tile_size * rows;\r\n\t\t\tbuffer.canvas.width  = tile_size * columns;\r\n\t\t\tbuffer.imageSmoothingEnabled = false;\r\n\r\n\t\t\ttile_sheet.addEventListener(\"load\", (event) => { loop(); });\r\n\r\n\t\t\ttile_sheet.src = \"shoot.png\";\r\n\r\n\t\t\tcontext.canvas.addEventListener(\"click\", (event) => {\r\n\r\n\t\t\t\tvar boundary = event.target.getBoundingClientRect();\r\n\r\n\t\t\t\tpointer.x = (event.pageX - boundary.left) / scale;\r\n\t\t\t\tpointer.y = (event.pageY - boundary.top) / scale;\r\n\r\n\t\t\t\tpointer.down = true;\r\n\r\n\t\t\t});\r\n\r\n\t\t</script>\r\n\r\n\t</body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/snake/snake.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  overflow:hidden;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  align-self:center;\r\n  background-color:#303840;\r\n  display:grid;\r\n  justify-self:center;\r\n\r\n}\r\n\r\n.hideable {\r\n\r\n  display:grid;\r\n\r\n}\r\n"
  },
  {
    "path": "content/snake/snake.html",
    "content": "<!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 name = \"description\" content = \"A simple snake game that runs in your browser! Check out the source code!\">\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <title>SNAKE</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1 class = \"hideable\">PoP Vlog - Snake</h1>\r\n\r\n    <p>SCORE: 0000</p>\r\n    <canvas></canvas>\r\n\r\n    <p class = \"hideable\">The goal of classic snake is to collect the segments to increase the length of your body.<br>NOTICE: Requires Keyboard</p>\r\n\r\n    <script src = \"snake.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/snake/snake.js",
    "content": "// Frank Poth 11/20/2017\r\n\r\n(function() {\r\n\r\n  /* I split the logic into three separate parts for organizational purposes. */\r\n  var controller, display, game;\r\n\r\n  /* The controller handles everything to do with getting user input. */\r\n  controller = {\r\n\r\n    down:false, left:false, right:false, up:false,\r\n\r\n    // A very simple key up/down event handler:\r\n    keyUpDown:function(event) {\r\n\r\n      var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left = key_state; break; // left key\r\n        case 38: controller.up = key_state; break; // up key\r\n        case 39: controller.right = key_state; break; // right key\r\n        case 40: controller.down = key_state; break; // down key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* display handles everything to do with the display. */\r\n  display = {\r\n\r\n    /* The buffer is where we draw everything to in world space. World space\r\n    is the actual size of the game world in pixels before it is scaled up to\r\n    match the size of the user's viewport. */\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    /* context is the drawing context of the display canvas. */\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    output:document.querySelector(\"p\"),// The output p element.\r\n\r\n    /* This object holds all of my graphics for the game. Each graphic is just\r\n    a different tile. Each graphic object has a canvas and a function to draw\r\n    itself. The numeric labels correspond to the tile values they represent.*/\r\n    graphics: {\r\n\r\n      0: {// background_tile\r\n\r\n        canvas:document.createElement(\"canvas\"),\r\n        draw:function() {\r\n\r\n          var context = this.canvas.getContext(\"2d\");\r\n          this.canvas.height = this.canvas.width = game.world.tile_size;\r\n\r\n          context.fillStyle = \"#202830\";\r\n          context.fillRect(0, 0, this.canvas.width, this.canvas.height);\r\n          context.fillStyle = \"#303840\";\r\n          context.fillRect(1, 1, this.canvas.width - 2, this.canvas.height - 2);\r\n\r\n        }\r\n\r\n      },\r\n\r\n      1: {// segment\r\n\r\n        canvas:document.createElement(\"canvas\"),\r\n        draw:function() {\r\n\r\n          var context = this.canvas.getContext(\"2d\");\r\n          this.canvas.height = this.canvas.width = game.world.tile_size;\r\n\r\n          context.fillStyle = \"#202830\";\r\n          context.fillRect(0, 0, this.canvas.width, this.canvas.height);\r\n          context.fillStyle = \"#ff9900\";\r\n          context.fillRect(1, 1, game.world.tile_size - 2, game.world.tile_size - 2);\r\n\r\n        }\r\n\r\n      },\r\n\r\n      2: {// apple\r\n\r\n        canvas:document.createElement(\"canvas\"),\r\n        draw:function() {\r\n\r\n          var context = this.canvas.getContext(\"2d\");\r\n          this.canvas.height = this.canvas.width = game.world.tile_size;\r\n\r\n          context.fillStyle = \"#202830\";\r\n          context.fillRect(0, 0, this.canvas.width, this.canvas.height);\r\n          context.fillStyle = \"#99ff00\";\r\n          context.fillRect(1, 1, game.world.tile_size - 2, game.world.tile_size - 2);\r\n\r\n        }\r\n\r\n      },\r\n\r\n    },\r\n\r\n    /* These are just here for ease of referencing the different graphics. I\r\n    figured it would be easier to remember variable names than numeric references. */\r\n    background_tile:0,\r\n    segment:1,\r\n    apple:2,\r\n\r\n    /* Renders everything to the buffer, and then renders the buffer to the display canvas. */\r\n    render:function() {\r\n\r\n      /* Loop through the tile map and draw the corresponding graphics. */\r\n      for (let index = 0; index < game.world.map.length; index ++) {\r\n\r\n        /* Get the appropriate graphics canvas from the graphics object. */\r\n        let graphic = this.graphics[game.world.map[index]].canvas;\r\n\r\n        /* Draw the tile at the specified x and y coordinates in the buffer using the 1d to 2d conversion formulas. */\r\n        this.buffer.drawImage(graphic, 0, 0, graphic.width, graphic.height, (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);\r\n\r\n      }\r\n\r\n      /* Output the score at the top of the screen, remembering to add leading zeros. */\r\n      let leading_zeros = \"SCORE: \";\r\n      for (let index = 4 - game.score.toString().length; index > 0; -- index) {\r\n\r\n        leading_zeros += \"0\";\r\n\r\n      }\r\n\r\n      this.output.innerHTML = leading_zeros + game.score;\r\n\r\n      /* Draw the finalized buffer to the display canvas. This takes care of scaling. */\r\n      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);\r\n\r\n    },\r\n\r\n    /* Handle resize events. */\r\n    resize:function(event) {\r\n\r\n      var client_height = document.documentElement.clientHeight;\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n      if (display.context.canvas.width > client_height - 64 || display.context.canvas.height > client_height - 64) {\r\n\r\n        display.context.canvas.width = client_height - 64;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = display.context.canvas.width;\r\n\r\n      display.render();\r\n\r\n      /* Hide some elements when the display canvas gets really big, like in full screen mode. */\r\n      let elements = document.querySelectorAll(\".hideable\");\r\n\r\n      for (let index = elements.length - 1; index > -1; -- index) {\r\n\r\n        if (document.body.offsetHeight < document.body.scrollHeight) {\r\n\r\n          elements[index].style.visibility = \"hidden\";\r\n\r\n        } else {\r\n\r\n          elements[index].style.visibility = \"visible\";\r\n\r\n        }\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* Everything to do with game logic goes here. */\r\n  game = {\r\n\r\n    /* Start the score off at 0. */\r\n    score:0,\r\n\r\n    /* The apple simply records a location on the map. */\r\n    apple: {\r\n\r\n      index:Math.floor(Math.random() * 400)\r\n\r\n    },\r\n\r\n    /* This is the snake object. It is fairly simple, tracking only map indices\r\n    and the movement vector. */\r\n    snake: {\r\n\r\n      head_index:209,\r\n      old_head_index:undefined,\r\n      segment_indices:[209, 210],\r\n      vector_x:0,\r\n      vector_y:0\r\n\r\n    },\r\n\r\n    /* The world object holds information about the level. */\r\n    world:{\r\n\r\n      columns:20,\r\n      tile_size:10,\r\n      map:new Array(400).fill(display.background_tile)// Creates a new array with 400 spaces filled with 0s.\r\n\r\n    },\r\n\r\n    /* The amount of time accumulated since the start of the application. */\r\n    accumulated_time:0,\r\n    time_step:250,/* The amount of time to wait between redraws. */\r\n\r\n    /* This resets the game. */\r\n    reset:function() {\r\n\r\n      this.score = 0;\r\n\r\n      /* Set all the tiles under the snake to zeros. */\r\n      for (let index = this.snake.segment_indices.length - 1; index > -1; -- index) {\r\n\r\n        this.world.map[this.snake.segment_indices[index]] = display.background_tile;\r\n\r\n      }\r\n\r\n      this.snake.segment_indices = [209, 210];\r\n      this.snake.head_index = 209;\r\n      this.snake.old_head_index = undefined;\r\n      this.snake.vector_x = this.snake.vector_y = 0;\r\n      this.world.map[game.apple.index] = display.apple;\r\n      this.world.map[game.snake.segment_indices[0]] = display.segment;\r\n      this.world.map[game.snake.segment_indices[1]] = display.segment;\r\n\r\n      this.time_step = 250;\r\n\r\n      this.loop();// Restart the game loop\r\n      display.render();\r\n\r\n    },\r\n\r\n    /* This is the game loop. All the cool stuff happens here. */\r\n    loop:function(time_stamp) {\r\n\r\n      /* Get controller input. Make sure that the vector on the opposite axis is\r\n      set to 0 so the snake doesn't move on a diagonal. */\r\n      if (controller.down) {\r\n\r\n        game.snake.vector_x = 0;\r\n        game.snake.vector_y = 1;\r\n\r\n      } else if (controller.left) {\r\n\r\n        game.snake.vector_x = -1;\r\n        game.snake.vector_y = 0;\r\n\r\n      } else if (controller.right) {\r\n\r\n        game.snake.vector_x = 1;\r\n        game.snake.vector_y = 0;\r\n\r\n      } else if (controller.up) {\r\n\r\n        game.snake.vector_x = 0;\r\n        game.snake.vector_y = -1;\r\n\r\n      }\r\n\r\n      /* Only redraw and update the game if enough time has passed. time_stamp\r\n      holds the amount of time in milliseconds that has passed since the start\r\n      of the application. */\r\n      if (time_stamp >= game.accumulated_time + game.time_step) {\r\n\r\n        game.accumulated_time = time_stamp;\r\n\r\n        /* If the snake isn't moving, there is nothing to update. */\r\n        if (game.snake.vector_x != 0 || game.snake.vector_y != 0) {\r\n\r\n          /* This allows the user to pause the game. If you try to move in the exact\r\n          opposite direction it sets the movement vector on each axis to 0. since\r\n          this is an early out case and nothing else needs to be executed afterwards,\r\n          we setup the next game loop and use return to exit the loop. */\r\n          if (game.snake.head_index + game.snake.vector_y * game.world.columns + game.snake.vector_x == game.snake.old_head_index) {\r\n\r\n            game.snake.vector_x = game.snake.vector_y = 0;\r\n            window.requestAnimationFrame(game.loop);\r\n            return;\r\n\r\n          }\r\n\r\n          /* We move the snake by removing its tail and placing it one step ahead\r\n          of its current head position in the direction of its movement vector. */\r\n          let tail_index = game.snake.segment_indices.pop();// remove the tail\r\n          game.world.map[tail_index] = display.background_tile;// set the tail index in the map to 0\r\n          game.snake.old_head_index = game.snake.head_index;// keep track of the last head index\r\n          game.snake.head_index += game.snake.vector_y * game.world.columns + game.snake.vector_x;// move the snake's head index\r\n\r\n          /* Do collision detection, testing to see if the snake's head index is\r\n          offscreen or colliding with another snake segment. */\r\n          if (game.world.map[game.snake.head_index] == display.segment// hit a segment\r\n            || game.snake.head_index < 0// off the top of the map\r\n            || game.snake.head_index > game.world.map.length - 1// off the bottom\r\n            || (game.snake.vector_x == -1 && game.snake.head_index % game.world.columns == game.world.columns - 1)// off the left of the map\r\n            || (game.snake.vector_x == 1 && (game.snake.head_index % game.world.columns == 0))) {// off the right\r\n\r\n            game.reset();\r\n            return;\r\n\r\n          }\r\n\r\n          /* Set the tile under the snake's head to a segment value. */\r\n          game.world.map[game.snake.head_index] = display.segment;\r\n          /* Put the snake's head index back into the front of its segment array. */\r\n          game.snake.segment_indices.unshift(game.snake.head_index);\r\n\r\n          /* Is the snake's head on the apple? */\r\n          if (game.snake.head_index == game.apple.index) {\r\n\r\n            game.score ++;\r\n            game.time_step = (game.time_step > 100)?game.time_step - 10:100;// increase speed\r\n\r\n            // add another segment to the tail position\r\n            game.snake.segment_indices.push(tail_index);\r\n            game.world.map[tail_index] = display.segment;\r\n            game.apple.index = Math.floor(Math.random() * game.world.map.length);// reset the apple\r\n\r\n            // If the snake fills up the entire map minus 1 space, reset the game.\r\n            if (game.snake.segment_indices.length == game.world.map.length - 1) {\r\n\r\n              game.reset();\r\n              return;\r\n\r\n            }\r\n\r\n            /* If the apple is on any tile but a background tile, we need to move\r\n            it to a background tile. We have to search for that background tile. */\r\n            while(game.world.map[game.apple.index] != display.background_tile) {\r\n\r\n              game.apple.index ++;\r\n\r\n              // If checking past the bottom of the map, jump to the top\r\n              if (game.apple.index > game.world.map.length - 1) {\r\n\r\n                game.apple.index = 0;\r\n\r\n              }\r\n\r\n            }\r\n\r\n            // Once the while loop ends, we know we have a safe spot for the apple.\r\n            game.world.map[game.apple.index] = display.apple;\r\n\r\n          }\r\n\r\n          display.render();\r\n\r\n        }\r\n\r\n      }\r\n\r\n      // Ensure the loop runs again.\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // Initialize the game:\r\n\r\n  display.buffer.canvas.height = display.buffer.canvas.width = game.world.columns * game.world.tile_size;\r\n\r\n  // Draw all the graphics.\r\n  for(let object in display.graphics) {\r\n\r\n    display.graphics[object].draw();\r\n\r\n  };\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n\r\n  game.reset();\r\n  display.resize();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/square-collision-response/response.css",
    "content": "/* 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  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-items:center;\r\n\r\n}\r\n\r\nh1 {\r\n\r\n  word-wrap:break-word;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  cursor:none;\r\n\r\n}\r\n"
  },
  {
    "path": "content/square-collision-response/response.html",
    "content": "<!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=device-width\">\r\n    <link href = \"response.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Square Collision Response</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"response.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/square-collision-response/response.js",
    "content": "// Frank Poth 08/30/2017\r\n\r\n// drawing context, controller object, Rectangle class,\r\n// the red and white rectangle objects, game loop, resize event handler\r\nvar context, controller, Rectangle, red, white, loop, resize;\r\n\r\ncontext = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n// keeps track of where the pointer is\r\ncontroller = {\r\n\r\n  // mouse or finger position\r\n  pointer_x:0,\r\n  pointer_y:0,\r\n\r\n  move:function(event) {\r\n\r\n    // This will give us the location of our canvas element on screen\r\n    var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n    // store the position of the move event inside the pointer variables\r\n    controller.pointer_x = event.clientX - rectangle.left;\r\n    controller.pointer_y = event.clientY - rectangle.top;\r\n\r\n  }\r\n\r\n};\r\n\r\n// A simple rectangle class\r\nRectangle = function(x, y, width, height, color) {\r\n\r\n  this.x = x;\r\n  this.y = y;\r\n  this.width = width;\r\n  this.height = height;\r\n\r\n  this.color = color;\r\n\r\n};\r\n\r\nRectangle.prototype = {\r\n\r\n  draw:function() {// draws rectangle to canvas\r\n\r\n    context.beginPath();\r\n    context.rect(this.x, this.y, this.width, this.height);\r\n    context.fillStyle = this.color;\r\n    context.fill();\r\n\r\n  },\r\n\r\n  // get the center coordinates of the rectangle\r\n  get centerX() { return this.x + this.width * 0.5; },\r\n  get centerY() { return this.y + this.height * 0.5; },\r\n  // get the four side coordinates of the rectangle\r\n  get bottom() { return this.y + this.height; },\r\n  get left() { return this.x; },\r\n  get right() { return this.x + this.width; },\r\n  get top() { return this.y; },\r\n\r\n  // determines if a collision is present between two rectangles\r\n  testCollision:function(rectangle) {\r\n\r\n    // using early outs cuts back on performance costs\r\n    if (this.top > rectangle.bottom || this.right < rectangle.left || this.bottom < rectangle.top || this.left > rectangle.right) {\r\n\r\n      return false;\r\n\r\n    }\r\n\r\n    return true;\r\n\r\n  },\r\n\r\n  // push the calling rectangle out of the callee rectangle on the\r\n  // axis that has the most overlap\r\n  resolveCollision:function(rectangle) {\r\n\r\n    var vector_x, vector_y;\r\n\r\n    // get the distance between center points\r\n    vector_x = this.centerX - rectangle.centerX;\r\n    vector_y = this.centerY - rectangle.centerY;\r\n\r\n    // is the y vector longer than the x vector?\r\n    if (vector_y * vector_y > vector_x * vector_x) {// square to remove negatives\r\n\r\n      // is the y vector pointing down?\r\n      if (vector_y > 0) {\r\n\r\n        this.y = rectangle.bottom;\r\n\r\n      } else { // the y vector is pointing up\r\n\r\n        this.y = rectangle.y - this.height;\r\n\r\n      }\r\n\r\n    } else { // the x vector is longer than the y vector\r\n\r\n      // is the x vector pointing right?\r\n      if (vector_x > 0) {\r\n\r\n        this.x = rectangle.right;\r\n\r\n      } else { // the x vector is pointing left\r\n\r\n        this.x = rectangle.x - this.width;\r\n\r\n      }\r\n\r\n    }\r\n\r\n  }\r\n\r\n};\r\n\r\n// create the rectangles, positioning the white one in the center of the screen\r\nred = new Rectangle(0, 0, 64, 64, \"#ff0000\");\r\nwhite = new Rectangle(context.canvas.width * 0.5 - 32, context.canvas.height * 0.5 - 32, 64, 64, \"#ffffff\");\r\n\r\n// the game loop\r\nloop = function(time_stamp) {\r\n\r\n  // make the red rectangle follow the pointer\r\n  red.x = controller.pointer_x - 32;\r\n  red.y = controller.pointer_y - 32;\r\n\r\n  // fill background color\r\n  context.fillStyle = \"#303840\";\r\n  context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n  white.draw();\r\n  red.draw();\r\n\r\n  // if there is a collision\r\n  if (red.testCollision(white)) {\r\n\r\n    // resolve the collision\r\n    red.resolveCollision(white);\r\n\r\n    // draw the white outlines around the two rectangles\r\n    context.beginPath();\r\n    context.rect(red.x, red.y, red.width, red.height);\r\n    context.rect(white.x, white.y, white.width, white.height);\r\n    context.lineWidth = 1;\r\n    context.strokeStyle = \"#ffffff\";\r\n    context.stroke();\r\n\r\n    // draw the collision regions of the white rectangle (white X)\r\n    context.beginPath();\r\n    context.moveTo(white.centerX - white.width, white.centerY - white.height);\r\n    context.lineTo(white.centerX + white.width, white.centerY + white.height);\r\n    context.stroke();\r\n\r\n    context.beginPath();\r\n    context.moveTo(white.centerX + white.width, white.centerY - white.height);\r\n    context.lineTo(white.centerX - white.width, white.centerY + white.height);\r\n    context.stroke();\r\n\r\n    // draw the line between the center points of the rectangles\r\n    context.beginPath();\r\n    context.moveTo(controller.pointer_x, controller.pointer_y);\r\n    context.lineTo(white.centerX, white.centerY);\r\n    context.lineWidth = 3;\r\n    context.strokeStyle = \"#303840\";\r\n    context.stroke();\r\n\r\n  }\r\n\r\n  // perpetuate loop\r\n  window.requestAnimationFrame(loop);\r\n\r\n};\r\n\r\n// just keeps the canvas element sized appropriately\r\nresize = function(event) {\r\n\r\n  context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n  if (context.canvas.width > document.documentElement.clientHeight) {\r\n\r\n    context.canvas.width = document.documentElement.clientHeight;\r\n\r\n  }\r\n\r\n  context.canvas.height = Math.floor(context.canvas.width * 0.5625);\r\n\r\n  white.x = context.canvas.width * 0.5 - 32;\r\n  white.y = context.canvas.height * 0.5 - 32;\r\n\r\n};\r\n\r\ncontext.canvas.addEventListener(\"mousemove\", controller.move);\r\ncontext.canvas.addEventListener(\"touchmove\", controller.move, {passive:true});\r\n\r\nwindow.addEventListener(\"resize\", resize, {passive:true});\r\n\r\nresize();\r\n\r\n// start the game loop\r\nwindow.requestAnimationFrame(loop);\r\n"
  },
  {
    "path": "content/starfield/starfield.html",
    "content": "<!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>StarField</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; }\r\n\r\n      html, body { width:100%; height:100%; }\r\n\r\n      canvas { height:100%; position:fixed; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      const Star = function(x, y, z) {\r\n\r\n        this.x = x; this.y = y; this.z = z;\r\n\r\n        this.size = 25;\r\n\r\n      };\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var height = document.documentElement.clientHeight;\r\n      var width  = document.documentElement.clientWidth;\r\n\r\n      var stars = new Array();\r\n\r\n      var max_depth = 7500;\r\n\r\n      for(let index = 0; index < 200; index ++) stars[index] = new Star(Math.random() * width, Math.random() * height, index * (max_depth / 200));\r\n\r\n      function loop() {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        height = document.documentElement.clientHeight;\r\n        width  = document.documentElement.clientWidth;\r\n\r\n        context.canvas.height = height;\r\n        context.canvas.width  = width;\r\n\r\n        context.fillStyle = \"#000000\";\r\n        context.fillRect(0, 0, width, height);\r\n\r\n        for (let index = stars.length - 1; index > -1; index --) {\r\n\r\n          let star = stars[index];\r\n\r\n          star.z -= 5;\r\n\r\n          if (star.z < 0) {\r\n\r\n            stars.push(stars.splice(index, 1)[0]);\r\n            star.z = max_depth;\r\n            continue;\r\n\r\n          }\r\n\r\n          let translate_x = width * 0.5;\r\n          let translate_y = height * 0.5;\r\n\r\n          let field_of_view = (height + width) * 0.5;\r\n\r\n          let star_x = (star.x - translate_x) / (star.z / field_of_view) + translate_x;\r\n          let star_y = (star.y - translate_y) / (star.z / field_of_view) + translate_y;\r\n\r\n          let scale = field_of_view / (field_of_view + star.z);\r\n\r\n          let color = Math.floor(scale * 256);\r\n\r\n          context.fillStyle = \"rgb(\" + color + \",\" + color + \",\" + color + \")\";\r\n          context.fillRect(star_x, star_y, star.size * scale, star.size * scale);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      loop();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/stay-down/game-states/pause.js",
    "content": "STAY_DOWN.initializers.pauseState = () => {\n\n  // buffers\n  const display_buffer = STAY_DOWN.buffers.display;\n  const text_buffer = STAY_DOWN.buffers.text;\n\n  // tools\n  const controller = STAY_DOWN.tools.controller;\n  const state_manager = STAY_DOWN.tools.state_manager;\n  const text = STAY_DOWN.tools.text;\n\n  // utilities\n  const Buffer = STAY_DOWN.utilities.Buffer;\n\n  function activate(message) {\n\n    Buffer.resize(text_buffer, 256, 256);\n    \n    text.write(text_buffer, 8, 20, 256, 'paused');\n    text.write(text_buffer, 8, 40, 256, 'press p to play');\n    text.write(text_buffer, 8, 60, 256, 'press q to quit');\n\n    Buffer.draw(display_buffer, text_buffer.canvas, 0, 0);\n\n  }\n\n  function deactivate() {}\n\n  function render() {}\n\n  function update() {\n\n    if (controller.getKey('p')) {\n      \n      controller.setKey('p', false);\n      \n      state_manager.change('run');\n\n    } else if (controller.getKey('q')) {\n\n      controller.setKey('q', false);\n\n      state_manager.change('title');\n\n    }\n  \n  }\n\n  STAY_DOWN.states.pause = { activate, deactivate, render, update };\n\n};"
  },
  {
    "path": "content/stay-down/game-states/run.js",
    "content": "STAY_DOWN.initializers.runState = () => {\n\n  // buffers\n  const background_buffer = STAY_DOWN.buffers.background;\n  const display_buffer = STAY_DOWN.buffers.display;\n  const spikes_buffer = STAY_DOWN.buffers.spikes;\n  const text_box_buffer = STAY_DOWN.buffers.text_box;\n  const text_buffer = STAY_DOWN.buffers.text;\n\n  // images\n  const dominique_image = STAY_DOWN.images.dominique;\n  const diamond_image = STAY_DOWN.images.diamond;\n  const platform_image = STAY_DOWN.images.platform;\n  \n  // tools\n  const controller = STAY_DOWN.tools.controller;\n  const state_manager = STAY_DOWN.tools.state_manager;\n  const text = STAY_DOWN.tools.text;\n  \n  // Utilities\n  const Buffer = STAY_DOWN.utilities.Buffer;\n  const Collider = STAY_DOWN.utilities.Collider;\n  const Item = STAY_DOWN.utilities.Item;\n  const Platform = STAY_DOWN.utilities.Platform;\n  const Player = STAY_DOWN.utilities.Player;\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\n\n  const friction = 0.8;\n  const gravity  = 1;\n  const world_width  = 256;\n  const world_height = 256;\n\n  const ground_top = world_height - 32;\n\n  const item = Item.create(128, 100);\n  const player = Player.create(2, ground_top - 32);\n\n  const platforms = [];\n\n  const hype_words = ['oooooh, yeah!!! i got $ diamonds!',\n  'that\\'s right, $ diamonds!',\n  'that makes $ diamonds.',\n  'hey, now! i just got $ diamonds!!!',\n  '$ diamonds!!!',\n  'how am i fitting these massive diamonds on my person?',\n  '$ diamonds! i wonder if something will happen if i get enough of these...',\n  'got another one, that\\'s $.'];\n\n  var item_count = 0;\n\n  for (let x = world_width - 19; x > 0; x -= 18) { platforms.push(Platform.create(x, ground_top, 16, 4)); }\n\n  function activate(message) {\n\n    Buffer.resize(text_buffer, 250, 24);\n\n    if (message === 'reset') reset();\n    else text.write(text_buffer, 0, 0, 250, 'why do i suddenly feel like i was frozen in time?');\n\n  }\n\n  function deactivate() {}\n\n  function render() {\n\n    Buffer.draw(display_buffer, background_buffer.canvas, 0, 0);\n\n    for (var index = platforms.length - 1; index > -1; -- index) {\n\n      var platform = platforms[index];\n\n      Buffer.draw(display_buffer, platform_image, platform.x, platform.y);\n\n    }\n\n    Buffer.draw(display_buffer, diamond_image, item.x, item.y);\n\n    Buffer.drawFlippedX(display_buffer, dominique_image, player.x, player.y, player.direction);\n    \n    Buffer.draw(display_buffer, spikes_buffer.canvas, 0, 0);\n    Buffer.draw(display_buffer, text_box_buffer.canvas, 0, 228);\n    Buffer.draw(display_buffer, text_buffer.canvas, 3, 230);\n\n    display_buffer.fillStyle = '#202830';\n    display_buffer.fillRect(0, ground_top, world_width, 4);\n\n  }\n\n  function reset() {\n\n    Rectangle2D.setLeft(player, 2);\n\n    Rectangle2D.setBottom(player, ground_top);\n\n    for (var p = platforms.length - 1; p > -1; -- p) Platform.reset(platforms[p], ground_top);\n\n    item_count = 0;\n    \n    text.write(text_buffer, 0, 0, 250, 'where am i? i need to get out of here! hey, is that a diamond?', true);\n\n  }\n\n  function update() {\n\n    // Pause\n    if (controller.getKey('p')) {\n     \n      controller.setKey('p', false);\n      \n      state_manager.change('pause');\n\n      return;\n\n    }\n\n    if (controller.getKey('left'))  Player.moveLeft(player);\n    if (controller.getKey('right')) Player.moveRight(player);\n\n    if (controller.getKey('up')) Player.jump(player);\n\n    if (Rectangle2D.getTop(player) < 4) {\n\n      if (item_count === 0) text.write(text_buffer, 0, 0, 250, 'ouch! i need to stay away from those!', true);\n      else text.write(text_buffer, 0, 0, 250, 'oh, snap! i lost my diamonds!!! also, that was incredibly painful!', true);\n\n      player.y = player.old_y = ground_top - player.height;\n      player.x = 2;\n\n      item_count = 0;\n\n    }\n\n    Player.updatePosition(player, gravity, friction);\n    Collider.keepPlayerInBounds(player, 0, 256);\n\n    Item.updatePosition(item, Rectangle2D.getCenterX(player), Rectangle2D.getCenterY(player), friction);\n    Collider.keepItemInBounds(item, 0, 256, 8, 232);\n\n    if (Collider.collideTop(player, ground_top)) Player.ground(player);\n\n    for (var index = platforms.length - 1; index > -1; -- index) {\n\n      var platform = platforms[index];\n\n      Platform.moveUp(platform);\n\n      if (platform.y < 0) Platform.reset(platform, ground_top);\n\n      if (Collider.collidePlayerWithPlatform(player, platform)) Player.ground(player, platform.velocity_y);\n\n    }\n\n    if (Collider.collideRectangleWithRectangle(item, player)) {\n\n      Item.reset(item, 0, 0, world_width, world_height);\n\n      item_count ++;\n\n      if (item_count === 1) text.write(text_buffer, 0, 0, 250, 'hey, it is a diamond! i should get more of these!', true);\n      else {\n       \n        var message = hype_words[Math.floor(Math.random() * hype_words.length)];\n\n        message = message.replace('$', item_count);\n\n        text.write(text_buffer, 0, 0, 250, message, true);\n\n      }\n\n    };\n\n  }\n\n  STAY_DOWN.states.run = { activate, deactivate, render, update };\n\n};"
  },
  {
    "path": "content/stay-down/game-states/title.js",
    "content": "STAY_DOWN.initializers.titleState = () => {\r\n\r\n  // buffers\r\n  const background_buffer = STAY_DOWN.buffers.background;\r\n  const display_buffer = STAY_DOWN.buffers.display;\r\n  const text_buffer = STAY_DOWN.buffers.text;\r\n\r\n  // images\r\n  const dominique_image = STAY_DOWN.images.dominique;\r\n  const diamond_image = STAY_DOWN.images.diamond;\r\n\r\n  // tools\r\n  const controller = STAY_DOWN.tools.controller;\r\n  const state_manager = STAY_DOWN.tools.state_manager;\r\n  const text = STAY_DOWN.tools.text;\r\n\r\n  // utilities\r\n  const Buffer = STAY_DOWN.utilities.Buffer;\r\n\r\n  function activate(message) {\r\n\r\n    Buffer.resize(text_buffer, 256, 256);\r\n    \r\n    text.write(text_buffer, 48, 100, 256, 'stay down by frank poth');\r\n    text.write(text_buffer, 72, 120, 256, 'press p to start');\r\n    text.write(text_buffer, 72, 140, 256, 'press p to pause');\r\n\r\n    Buffer.draw(display_buffer, background_buffer.canvas, 0, 0);\r\n    Buffer.draw(display_buffer, dominique_image, 4, 224);\r\n    Buffer.draw(display_buffer, diamond_image, 236, 236);\r\n    Buffer.draw(display_buffer, text_buffer.canvas, 0, 0);\r\n\r\n  }\r\n\r\n  function deactivate() {}\r\n\r\n  function render() {}\r\n\r\n  function update() {\r\n\r\n    if (controller.getKey('p')) {\r\n      \r\n      controller.setKey('p', false);\r\n      \r\n      state_manager.change('run', 'reset');\r\n\r\n    }\r\n  \r\n  }\r\n\r\n  STAY_DOWN.states.title = { activate, deactivate, render, update };\r\n\r\n};"
  },
  {
    "path": "content/stay-down/initialize.js",
    "content": "(() => {\r\n\r\n  const BUFFERS = STAY_DOWN.buffers;\r\n  const IMAGES = STAY_DOWN.images;\r\n  const INITIALIZERS = STAY_DOWN.initializers;\r\n  const TOOLS = STAY_DOWN.tools;\r\n\r\n  const Buffer = STAY_DOWN.utilities.Buffer;\r\n\r\n  INITIALIZERS.colliderUtility();\r\n  INITIALIZERS.itemUtility();\r\n  INITIALIZERS.platformUtility();\r\n  INITIALIZERS.playerUtility();\r\n\r\n  INITIALIZERS.stateManagerTool();\r\n\r\n  // I have no need to ever reference this again, so I'm defining it here.\r\n  function resize(event) {\r\n\r\n    const display = BUFFERS.display;\r\n    \r\n    const width_ratio = document.documentElement.clientWidth / display.canvas.width;\r\n    const height_ratio = document.documentElement.clientHeight / display.canvas.height;\r\n    \r\n    const scale = width_ratio < height_ratio ? width_ratio : height_ratio;\r\n    \r\n    display.canvas.style.height = Math.floor(display.canvas.width * scale) + 'px';\r\n    display.canvas.style.width = Math.floor(display.canvas.height * scale) + 'px';\r\n\r\n  }\r\n\r\n  window.addEventListener('resize', resize);\r\n\r\n  // load all the images\r\n  TOOLS.loader.loadImages([\r\n\r\n    'media/images/diamond.png',\r\n    'media/images/dominique.png',\r\n    'media/images/platform.png',\r\n    'media/images/spike.png',\r\n    'media/images/tile.png',\r\n    'media/images/text.png',\r\n    'media/images/text-box.png'\r\n\r\n  ],\r\n  // the callback function to handle the images once they're loaded  \r\n  function(images_) {\r\n\r\n    IMAGES.diamond = images_[0];\r\n    IMAGES.dominique = images_[1];\r\n    IMAGES.platform = images_[2];\r\n    IMAGES.spike = images_[3];\r\n    IMAGES.tile = images_[4];\r\n    IMAGES.text = images_[5];\r\n    IMAGES.text_box = images_[6];\r\n\r\n    INITIALIZERS.textTool(); // Uses the text image, so must be initialized after loading the text image.\r\n\r\n    BUFFERS.background = Buffer.create(256, 256, false, true);\r\n    BUFFERS.display = Buffer.create(256, 256, false, false);\r\n    BUFFERS.spikes = Buffer.create(256, 8, true, true);\r\n    BUFFERS.text = Buffer.create(0, 0, true, true);\r\n    BUFFERS.text_box = Buffer.create(256, 32, false, true);\r\n\r\n    // These use the text tool as well as some buffers, so we have to initialize them after the text tool.\r\n    INITIALIZERS.runState();\r\n    INITIALIZERS.pauseState();\r\n    INITIALIZERS.titleState();\r\n\r\n    document.body.appendChild(BUFFERS.display.canvas);\r\n\r\n    // once we have images, we can set up buffer images\r\n    var buffer = BUFFERS.background;\r\n    var image = IMAGES.tile;\r\n\r\n    var x, y;\r\n\r\n    for (x = 240; x > -1; x -= 16) for (y = 240; y > -1; y -= 16) {\r\n\r\n      var random_x = Math.floor(Math.random() * 3) * 16;\r\n\r\n      buffer.drawImage(image, random_x, 0, 16, 16, x, y, 16, 16);\r\n\r\n    }\r\n\r\n    buffer = BUFFERS.spikes;\r\n    image = IMAGES.spike;\r\n\r\n    for (x = 240; x > -1; x -= 16) buffer.drawImage(image, x, 0);\r\n\r\n    buffer = BUFFERS.text_box;\r\n    image = IMAGES.text_box;\r\n\r\n    buffer.drawImage(image, 32, 0, 16, 28, 240, 0, 16, 28);\r\n\r\n    for (x = 224; x > 0; x -= 16) buffer.drawImage(image, 16, 0, 16, 28, x, 0, 16, 28);\r\n\r\n    buffer.drawImage(image, 0, 0, 16, 28, 0, 0, 16, 28);\r\n\r\n    resize();\r\n\r\n    TOOLS.controller.activate();\r\n  \r\n    TOOLS.state_manager.change('title');\r\n  \r\n    TOOLS.engine.start();\r\n\r\n  });\r\n\r\n})();"
  },
  {
    "path": "content/stay-down/stay-down.html",
    "content": "<!DOCTYPE html>\n\n<html>\n\n  <head>\n\n    <meta charset = 'utf-8'>\n    <meta name = 'viewport' content = 'width=device-width'>\n\n    <title>Stay Down</title>\n\n    <style>\n\n      * { margin:0; padding:0; user-select:none; overflow:hidden; }\n\n      html, body {\n\n        width:100%;\n        height:100%;\n\n      }\n\n      body {\n\n        align-items: center;\n        background-color:#202830;\n        display:grid;\n        justify-items: center;\n\n      }\n\n      canvas {\n\n        image-rendering:crisp-edges;\n        image-rendering:pixelated;\n\n      }\n\n    </style>\n\n    <script src='stay-down.js'></script>\n\n    <script src='game-states/pause.js'></script>\n    <script src='game-states/run.js'></script>\n    <script src='game-states/title.js'></script>\n\n    <script src='tools/controller.js'></script>\n    <script src='tools/engine.js'></script>\n    <script src='tools/loader.js'></script>\n    <script src='tools/state-manager.js'></script>\n    <script src='tools/text.js'></script>\n\n    <script src='utilities/buffer.js'></script>\n    <script src='utilities/collider.js'></script>\n    <script src='utilities/frame.js'></script>\n    <script src='utilities/item.js'></script>\n    <script src='utilities/platform.js'></script>\n    <script src='utilities/player.js'></script>\n    <script src='utilities/rectangle-2d.js'></script>\n\n  </head>\n\n  <body>\n\n    <script src='initialize.js'></script>\n\n  </body>\n\n</html>"
  },
  {
    "path": "content/stay-down/stay-down.js",
    "content": "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",
    "content": "STAY_DOWN.tools.controller = (() => {\n\n  const Input = () => ({ active:false, state:false });\n\n  const keys = {\n\n    'left':Input(),\n    'p':Input(),\n    'q':Input(),\n    'right':Input(),\n    'up':Input()\n\n  }\n\n  function activate() {\n\n    window.addEventListener('keydown', keyDownUp);\n    window.addEventListener('keyup', keyDownUp);\n\n  }\n\n  function getKey(name) { return keys[name].active };\n\n  function keyDownUp(event) { event.preventDefault();\n\n    var state = event.type == 'keydown';\n\n    switch(event.keyCode) {\n\n      case 37: trigger(keys.left, state); break;\n      case 38: trigger(keys.up, state); break;\n      case 39: trigger(keys.right, state); break;\n      case 80: trigger(keys.p, state); break;\n      case 81: trigger(keys.q, state);\n\n    }\n    \n  }\n\n  function setKey(name, active) { keys[name].active = active; }\n\n  function trigger(input, state) { if (state !== input.state) input.active = input.state = state; }\n\n  return { activate, getKey, setKey };\n\n})();"
  },
  {
    "path": "content/stay-down/tools/engine.js",
    "content": "STAY_DOWN.tools.engine = (() => {\n\n  var running = false;\n\n  var raf_handle;\n\n  var accumulated_time = 0;\n  var current_time = 0;\n  var time_step = 1000/60;\n\n  var state;\n  \n  function cycle(time_stamp) {\n  \n    raf_handle = window.requestAnimationFrame(cycle);\n  \n    accumulated_time += time_stamp - current_time;\n    current_time = time_stamp;\n  \n    var updated = false;\n  \n    if (accumulated_time > 60) accumulated_time = time_step;\n  \n    while(accumulated_time >= time_step) {\n  \n      state.update();\n  \n      updated = true;\n  \n      accumulated_time -= time_step;\n  \n    }\n  \n    if (updated) state.render();\n  \n  }\n\n  function start() {\n  \n    running = true;\n    raf_handle = window.requestAnimationFrame(cycle);\n  \n  }\n\n  function stop() {\n    \n    running = false;\n    window.cancelAnimationFrame(raf_handle);\n  \n  }\n\n  function setState(state_) { state = state_; }\n\n  return { start, stop, setState };\n\n})();"
  },
  {
    "path": "content/stay-down/tools/loader.js",
    "content": "STAY_DOWN.tools.loader = (() => {\n\n  function loadImages(urls, callback) {\n\n    var images = [];\n    var counter = urls.length;\n\n    function resolve(event) {\n\n      var image = event.target;\n\n      image.removeEventListener('load', resolve);\n      image.removeEventListener('error', resolve);\n\n      counter --;\n\n      if (counter === 0) callback(images);\n\n    }\n\n    for (var index = urls.length - 1; index > -1; index --) {\n\n      var image = images[index] = new Image();\n\n      image.addEventListener('load', resolve);\n      image.addEventListener('error', resolve);\n\n      image.src = urls[index];\n\n    }\n    \n  }\n\n  return { loadImages };\n\n})();"
  },
  {
    "path": "content/stay-down/tools/state-manager.js",
    "content": "STAY_DOWN.initializers.stateManagerTool = () => {\r\n\r\n  const ENGINE = STAY_DOWN.tools.engine;\r\n  const STATES = STAY_DOWN.states;\r\n  \r\n  var state;\r\n  \r\n  function change(name, message) {\r\n  \r\n    if (state) state.deactivate();\r\n  \r\n    state = STATES[name];\r\n  \r\n    state.activate(message);\r\n  \r\n    ENGINE.setState(state);\r\n  \r\n  };\r\n\r\n  STAY_DOWN.tools.state_manager = { change };\r\n\r\n};"
  },
  {
    "path": "content/stay-down/tools/text.js",
    "content": "STAY_DOWN.initializers.textTool = () => {\n\n  const image = STAY_DOWN.images.text;\n  const Frame = STAY_DOWN.utilities.Frame;\n\n  const letter_spacing = 1;\n  const line_height = 10;\n  const space_width = 3;\n  \n  const frames = {\n  \n    'a':Frame.create(0,3,6,6,0,3),\n    'b':Frame.create(7,0,5,9),\n    'c':Frame.create(13,4,5,5,0,4),\n    'd':Frame.create(19,0,5,9),\n    'e':Frame.create(25,4,5,5,0,4),\n    'f':Frame.create(31,0,5,9),\n    'g':Frame.create(37,1,5,8,0,4),\n    'h':Frame.create(43,0,5,9),\n    'i':Frame.create(49,2,1,7,0,2),\n    'j':Frame.create(51,0,4,9,-2,2),\n    'k':Frame.create(56,0,5,9),\n    'l':Frame.create(62,0,1,9),\n    'm':Frame.create(64,4,9,5,0,4),\n    'n':Frame.create(74,4,5,5,0,4),\n    'o':Frame.create(80,4,5,5,0,4),\n    'p':Frame.create(86,0,5,9,0,4),\n    'q':Frame.create(92,0,5,9,0,4),\n    'r':Frame.create(98,4,4,5,0,4),\n    's':Frame.create(103,4,5,5,0,4),\n    't':Frame.create(109,0,5,9),\n    'u':Frame.create(115,4,5,5,0,4),\n    'v':Frame.create(121,4,5,5,0,4),\n    'w':Frame.create(127,4,9,5,0,4),\n    'x':Frame.create(137,4,5,5,0,4),\n    'y':Frame.create(143,1,5,8,0,4),\n    'z':Frame.create(149,4,4,5,0,4),\n    '0':Frame.create(154,0,5,9),\n    '1':Frame.create(160,0,3,9),\n    '2':Frame.create(164,0,5,9),\n    '3':Frame.create(170,0,5,9),\n    '4':Frame.create(176,0,4,9),\n    '5':Frame.create(181,0,5,9),\n    '6':Frame.create(187,0,5,9),\n    '7':Frame.create(194,0,5,9),\n    '8':Frame.create(199,0,5,9),\n    '9':Frame.create(205,0,5,9),\n    '!':Frame.create(211,0,1,9),\n    '?':Frame.create(213,1,5,8,0,1),\n    '.':Frame.create(1,3,1,1,0,8),\n    \"'\":Frame.create(4,4,1,2,0,2),\n    ',':Frame.create(4,4,1,2,0,8)\n  \n  };\n  \n  function write(context, position_x, position_y, width, string, clear = false) {\n  \n    var start_x = position_x;\n  \n    if (clear) context.clearRect(0, 0, context.canvas.width, context.canvas.height);\n  \n    var length = string.length;\n  \n    for (var i = 0; i < length; i ++) {\n  \n      var char = string.charAt(i);\n  \n      if (char === ' ') { position_x += space_width; continue; }\n  \n      var frame = frames[char];\n  \n      if (!frame) continue;\n  \n      context.drawImage(image, frame.x, frame.y, frame.width, frame.height, position_x + frame.offset_x, position_y + frame.offset_y, frame.width, frame.height);\n  \n      position_x += frame.width + letter_spacing;\n  \n      if (position_x > width) {\n  \n        position_x = start_x;\n        position_y += line_height;\n  \n      }\n  \n    }\n  \n  }\n\n  STAY_DOWN.tools.text = { write };\n\n};"
  },
  {
    "path": "content/stay-down/utilities/buffer.js",
    "content": "STAY_DOWN.utilities.Buffer = {\r\n\r\n  create(width, height, alpha = false, desynchronized = true) {\r\n\r\n    const buffer = document.createElement('canvas').getContext('2d', { alpha, desynchronized });\r\n  \r\n    buffer.canvas.height = height;\r\n    buffer.canvas.width = width;\r\n    buffer.imageSmoothingEnabled = false;\r\n  \r\n    return buffer;\r\n  \r\n  },\r\n\r\n  draw(b, image, x, y) {\r\n\r\n    b.drawImage(image, Math.floor(x), Math.floor(y));\r\n\r\n  },\r\n\r\n  drawFlippedX(b, image, x, y, scale_x) {\r\n\r\n    if (scale_x === -1) {\r\n      \r\n      b.scale(-1, 1);\r\n      b.drawImage(image, Math.floor(-Math.floor(x) - image.width), Math.floor(y));\r\n      b.scale(-1, 1);\r\n\r\n    } else b.drawImage(image, Math.floor(x), Math.floor(y));\r\n\r\n  },\r\n\r\n  resize(b, width, height, smoothing = false) {\r\n\r\n    b.canvas.width = width;\r\n    b.canvas.height = height;\r\n    b.imageSmoothingEnabled = smoothing;\r\n\r\n  }\r\n  \r\n};"
  },
  {
    "path": "content/stay-down/utilities/collider.js",
    "content": "STAY_DOWN.initializers.colliderUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOWN.utilities.Collider = {\r\n\r\n    collideRectangleWithRectangle(r1, r2) {\r\n\r\n      if (Rectangle2D.getLeft(r1) > Rectangle2D.getRight(r2) || Rectangle2D.getTop(r1) > Rectangle2D.getBottom(r2) || Rectangle2D.getRight(r1) < Rectangle2D.getLeft(r2) || Rectangle2D.getBottom(r1) < Rectangle2D.getTop(r2)) return false;\r\n\r\n      return true;\r\n\r\n    },\r\n\r\n    collideTop(rectangle, top) {\r\n\r\n      if (Rectangle2D.getBottom(rectangle) > top) {\r\n\r\n        Rectangle2D.setBottom(rectangle, top);\r\n\r\n        return true;\r\n\r\n      } return false;\r\n\r\n    },\r\n\r\n    collidePlayerWithPlatform(player, platform) {\r\n\r\n      if (Rectangle2D.getRight(player) < Rectangle2D.getLeft(platform) || Rectangle2D.getLeft(player) > Rectangle2D.getRight(platform) || Rectangle2D.getBottom(player) < Rectangle2D.getTop(platform) || Rectangle2D.getOldBottom(player) > Rectangle2D.getOldTop(platform)) return false;\r\n\r\n      Rectangle2D.setBottom(player, Rectangle2D.getTop(platform));\r\n\r\n      return true;\r\n\r\n    },\r\n\r\n    keepItemInBounds(item, left, right, top, bottom) {\r\n\r\n      if (Rectangle2D.getLeft(item) < left) {\r\n        \r\n        item.velocity_x += 1;\r\n        item.rotation += Math.PI;\r\n\r\n      } else if (Rectangle2D.getRight(item) > right) {\r\n        \r\n        item.rotation += Math.PI;\r\n        item.velocity_x -= 1;\r\n\r\n      }\r\n\r\n      if (Rectangle2D.getTop(item) < top) {\r\n        \r\n        item.rotation += Math.PI;\r\n        item.velocity_y += 1;\r\n      \r\n      }  else if (Rectangle2D.getBottom(item) > bottom) {\r\n       \r\n        item.rotation += Math.PI;\r\n        item.velocity_y -= 1;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    keepPlayerInBounds(player, left, right) {\r\n\r\n      if (Rectangle2D.getLeft(player) < left) player.velocity_x += 1;\r\n      else if (Rectangle2D.getRight(player) > right) player.velocity_x -= 1;\r\n\r\n    }\r\n\r\n  };\r\n\r\n};"
  },
  {
    "path": "content/stay-down/utilities/frame.js",
    "content": "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, width, height, offset_x, offset_y };\r\n\r\n  }\r\n\r\n};"
  },
  {
    "path": "content/stay-down/utilities/item.js",
    "content": "STAY_DOWN.initializers.itemUtility = () => {\n\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\n  \n  STAY_DOWN.utilities.Item = {\n    \n    create(x, y) {\n\n      return {\n\n        r:0,\n        velocity_r:0,\n        velocity_x:0,\n        velocity_y:0,\n      \n        ...Rectangle2D.create(x, y, 16, 16)\n\n      };\n\n    },\n\n    reset(i, left, top, right, bottom) {\n\n      i.velocity_x = 0;\n      i.velocity_y = 0;\n      i.x = Math.random() * right;\n      i.y = Math.random() * bottom;\n\n    },\n\n    updatePosition(i, x, y, friction) {\n\n      var cx = Rectangle2D.getCenterX(i);\n      var cy = Rectangle2D.getCenterY(i);\n\n      var v1_x = cx - x;\n      var v1_y = cy - y;\n      var v2_x = 1;\n      var v2_y = 0;\n\n      var distance = v1_x * v1_x + v1_y * v1_y;\n\n      var cross_product = v1_x * v2_y - v1_y * v2_x;\n      var dot_product = v1_x * v2_x + v1_y * v2_y;\n\n      var speed = 1000/distance;\n\n      if (distance < 5000) {\n\n        i.r = Math.atan2(Math.abs(cross_product), dot_product);\n\n        if (cross_product > 0) i.r = Math.PI * 2 - i.r;\n\n      }\n\n      i.velocity_r += Math.random() * 1 - 0.5;\n\n      i.r += i.velocity_r;\n\n      i.velocity_x += Math.cos(i.r) * speed;\n      i.velocity_y += Math.sin(i.r) * speed;\n\n      i.velocity_r *= friction;\n      i.velocity_x *= friction;\n      i.velocity_y *= friction;\n\n      i.x += i.velocity_x;\n      i.y += i.velocity_y;\n\n    }\n\n  };\n\n};"
  },
  {
    "path": "content/stay-down/utilities/platform.js",
    "content": "STAY_DOWN.initializers.platformUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOWN.utilities.Platform = {\r\n\r\n    create(x, y) {\r\n\r\n      return {\r\n\r\n        move_force:Math.random() * 0.05 + 0.01,\r\n        velocity_y:0,\r\n        velocity_y_max:-Math.random() * 2 - 1,\r\n      \r\n        ...Rectangle2D.create(x, y, 16, 16)\r\n      \r\n      };\r\n\r\n    },\r\n\r\n    moveUp(p) {\r\n\r\n      p.velocity_y -= p.move_force;\r\n      \r\n      if (p.velocity_y < p.velocity_y_max) p.velocity_y = p.velocity_y_max;\r\n  \r\n      Rectangle2D.moveY(p, p.velocity_y);\r\n  \r\n    },\r\n  \r\n    reset(p, y) {\r\n  \r\n      p.velocity_y = 0;\r\n      p.velocity_y_max = -Math.random() * 2 - 1;\r\n      Rectangle2D.setTop(p, y);\r\n  \r\n    }\r\n\r\n  };\r\n\r\n};"
  },
  {
    "path": "content/stay-down/utilities/player.js",
    "content": "STAY_DOWN.initializers.playerUtility = () => {\r\n\r\n  const Rectangle2D = STAY_DOWN.utilities.Rectangle2D;\r\n\r\n  STAY_DOWN.utilities.Player = {\r\n\r\n    create(x, y) {\r\n\r\n      return {\r\n  \r\n        color:'#ff0000',\r\n        direction:1,\r\n        grounded:false,\r\n        move_force:1,\r\n        jump_force:18,\r\n        velocity_x:0,\r\n        velocity_y:0,\r\n      \r\n        ...Rectangle2D.create(x, y, 16, 32)\r\n      \r\n      }\r\n\r\n    },\r\n    \r\n    ground(p, velocity_y = 0) {\r\n        \r\n      p.grounded   = true;\r\n      p.velocity_y = velocity_y;\r\n    \r\n    },\r\n\r\n    jump(p) {\r\n\r\n      if (p.grounded) {\r\n\r\n        p.grounded    = false;\r\n        p.velocity_y -= p.jump_force;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    moveLeft(p) {\r\n      \r\n      p.direction = -1;\r\n      p.velocity_x -= p.move_force;\r\n    \r\n    },\r\n\r\n    moveRight(p) {\r\n      \r\n      p.direction = 1;\r\n      p.velocity_x += p.move_force;\r\n    \r\n    },\r\n\r\n    updatePosition(p, gravity, friction) {\r\n\r\n      p.velocity_x *= friction;\r\n      p.velocity_y += gravity;\r\n      p.velocity_y *= friction;\r\n\r\n      p.x += p.velocity_x;\r\n      \r\n      Rectangle2D.moveY(p, p.velocity_y);\r\n\r\n    }\r\n\r\n  }\r\n\r\n};"
  },
  {
    "path": "content/stay-down/utilities/rectangle-2d.js",
    "content": "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      old_y:y,\r\n      width:width,\r\n      x:x,\r\n      y:y\r\n      \r\n    };\r\n\r\n  },\r\n\r\n  getBottom(r) { return r.y + r.height; },\r\n  getCenterX(r) { return r.x + r.width * 0.5; },\r\n  getCenterY(r) { return r.y + r.height * 0.5; },\r\n  getLeft(r) { return r.x; },\r\n  getRight(r) { return r.x + r.width; },\r\n  getTop(r) { return r.y; },\r\n\r\n  getOldBottom(r) { return r.old_y + r.height; },\r\n  getOldTop(r) { return r.old_y; },\r\n\r\n  setBottom(r, y) { r.y = y - r.height; },\r\n  setLeft(r, x) { r.x = x; },\r\n  setRight(r, x) { r.x = x - r.width; },\r\n  setTop(r, y) { r.y = y; },\r\n\r\n  moveX(r, x) { r.x += x; },\r\n  moveY(r, y) { r.old_y = r.y; r.y += y; }\r\n\r\n};"
  },
  {
    "path": "content/tile-animation/tile-animation.html",
    "content": "<!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 Animation</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; overflow:hidden; }\r\n\r\n      body, html { height:100%; width:100%; }\r\n\r\n      body { background-color:#000000; display:grid; }\r\n\r\n      canvas { align-self:center; justify-self:center; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Animator {\r\n\r\n        constructor(frame_set, delay) {\r\n          \r\n          this.count = 0;\r\n          this.delay = delay;\r\n\r\n          this.frame_set = frame_set;// animation frames\r\n          this.frame_index = 0;// playhead\r\n          this.frame_value = frame_set[0];// current frame\r\n\r\n        }\r\n\r\n        animate() {\r\n\r\n          this.count ++;\r\n\r\n          if (this.count > this.delay) {\r\n\r\n            this.count = 0;\r\n\r\n            this.frame_index = (this.frame_index == this.frame_set.length - 1) ? 0 : this.frame_index + 1;\r\n            this.frame_value = this.frame_set[this.frame_index];\r\n\r\n\r\n          }\r\n\r\n        }\r\n\r\n      }\r\n\r\n      var buffer  = document.createElement(\"canvas\").getContext(\"2d\");\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var source_columns = 8;\r\n      var tile_sheet = new Image();\r\n      var tile_size  = 16;\r\n\r\n      var map_columns = 10;\r\n      var map_rows    = 10;\r\n      var map_ratio   = map_columns / map_rows; // columns / rows\r\n      var map = [ 6, 0,22, 8, 8,21, 1, 5, 0,22,\r\n                 20,14,21, 8, 8,21, 8,12, 7,22,\r\n                 21,22,21, 8, 8,22, 8,19,14,21,\r\n                  0, 1, 5,15,15, 0, 8,21,22,22,\r\n                  7, 8,12,13,13, 7, 8,22, 1, 1,\r\n                 14, 8,12,13,13, 7, 8,21, 8, 8,\r\n                 22, 8,19,20,20,14, 8,21, 8, 8,\r\n                 21, 8,21,22,21,21, 8, 5,15,15,\r\n                  1, 8, 1, 5, 0,21, 8,19,20,20,\r\n                  8, 8, 8,12, 7,21, 8,21,22,21];\r\n\r\n       var animations = {\r\n\r\n         1: new Animator([1, 2, 3, 4], 10),\r\n         8: new Animator([8, 9,10,11], 10),\r\n         15: new Animator([15, 16, 17, 18], 10),\r\n         22: new Animator([22, 23], 40)\r\n\r\n       };\r\n\r\n      let screen_h = document.documentElement.clientHeight - 16;\r\n      let screen_w = document.documentElement.clientWidth  - 16;\r\n\r\n      function loop() {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        screen_h = document.documentElement.clientHeight - 16;\r\n        screen_w = document.documentElement.clientWidth  - 16;\r\n\r\n        let ratio = screen_w / screen_h;\r\n\r\n        if (screen_h / map_rows * tile_size < screen_w / map_columns * tile_size) {\r\n\r\n            context.canvas.height = screen_h;\r\n            context.canvas.width  = map_ratio * screen_h;\r\n\r\n        } else {\r\n\r\n            context.canvas.height = screen_w / map_ratio;\r\n            context.canvas.width  = screen_w;\r\n\r\n        }\r\n\r\n        context.imageSmoothingEnabled = false;\r\n        \r\n        Object.values(animations).forEach(animator => {\r\n\r\n          animator.animate();\r\n          \r\n        });\r\n\r\n\r\n        for (let index = map.length - 1; index > -1; -- index) {\r\n\r\n          let value = map[index];\r\n\r\n          if (animations[value]) value = animations[value].frame_value;\r\n\r\n          let source_x =           (value % source_columns) * tile_size;\r\n          let source_y = Math.floor(value / source_columns) * tile_size;\r\n          let dest_x   =           (index % map_columns)    * tile_size;\r\n          let dest_y   = Math.floor(index / map_columns)    * tile_size;\r\n\r\n          buffer.drawImage(tile_sheet, source_x, source_y, tile_size, tile_size, dest_x, dest_y, tile_size, tile_size);\r\n\r\n        }\r\n\r\n        context.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n      }\r\n\r\n      buffer.canvas.height = map_rows    * tile_size;\r\n      buffer.canvas.width  = map_columns * tile_size;\r\n      buffer.imageSmoothingEnabled = false;\r\n\r\n      tile_sheet.addEventListener(\"load\", event => {\r\n\r\n        loop();\r\n\r\n      });\r\n\r\n      tile_sheet.src = \"animated-tile.png\";\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/tile-graphics/tile-graphics.css",
    "content": "/* 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%; width:100%; }\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  align-items:center;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nh1 {\r\n\r\n  font-size:3.0em;\r\n  text-align:center;\r\n\r\n}\r\n"
  },
  {
    "path": "content/tile-graphics/tile-graphics.html",
    "content": "<!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    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <meta name = \"description\" content = \"Learn how to blit tile graphics to the screen from a 1 dimensional tile map!\">\r\n\r\n    <title>PoP Vlog - Tile Graphics</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog<br>Tile Graphics</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>This example simply blits tile graphics to the screen from a 1 dimensional tile map array.<br>You can view the page source with your browser's developer tools.</p>\r\n\r\n    <script src = \"tile-graphics.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/tile-graphics/tile-graphics.js",
    "content": "// 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. The buffer canvas is then blitted to the display canvas. */\r\n\r\n(function() { \"use strict\";\r\n\r\n  /* The display handles everything to do with drawing graphics and resizing the\r\n  screen. The world holds the map and its dimensions. */\r\n  var display, world;\r\n\r\n  display = {\r\n\r\n    /* We draw the tiles to the buffer in \"world\" coordinates or unscaled coordinates.\r\n    All scaling is handled by drawImage when we draw the buffer to the display canvas. */\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    /* Scaling takes place on the display canvas. This is its drawing context. The\r\n    height_width_ratio is used in scaling the buffer to the canvas. */\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    /* The height width ratio is the height to width ratio of the tile map. It is\r\n    used to size the display canvas to match the aspect ratio of the game world. */\r\n    height_width_ratio:undefined,\r\n\r\n    /* The tile_sheet object holds the tile sheet graphic as well as its dimensions. */\r\n    tile_sheet: {\r\n\r\n      image:new Image(),// The actual graphic will be loaded into this.\r\n\r\n      columns:3,\r\n      tile_height:16,\r\n      tile_width:16\r\n\r\n    },\r\n\r\n    /* This function draws the tile graphics from the tile_sheet.image to the buffer\r\n    one by one according to the world.map. It then draws the buffer to the display\r\n    canvas and takes care of scaling the buffer image up to the display canvas size. */\r\n    render:function() {\r\n\r\n      /* Here we loop through the tile map. */\r\n      for (let index = world.map.length - 1; index > -1; -- index) {\r\n\r\n        /* We get the value of each tile in the map which corresponds to the tile\r\n        graphic index in the tile_sheet.image. */\r\n        var value = world.map[index];\r\n\r\n        /* This is the x and y location at which to cut the tile image out of the\r\n        tile_sheet.image. */\r\n        var source_x = (value % this.tile_sheet.columns) * this.tile_sheet.tile_width;\r\n        var source_y = Math.floor(value / this.tile_sheet.columns) * this.tile_sheet.tile_height;\r\n\r\n        /* This is the x and y location at which to draw the tile image we are cutting\r\n        from the tile_sheet.image to the buffer canvas. */\r\n        var destination_x = (index % world.columns) * this.tile_sheet.tile_width;\r\n        var destination_y = Math.floor(index / world.columns) * this.tile_sheet.tile_height;\r\n\r\n        /* Draw the tile image to the buffer. The width and height of the tile is taken from the tile_sheet object. */\r\n        this.buffer.drawImage(this.tile_sheet.image, source_x, source_y, this.tile_sheet.tile_width, this.tile_sheet.tile_height, destination_x, destination_y, this.tile_sheet.tile_width, this.tile_sheet.tile_height);\r\n\r\n      }\r\n\r\n      /* Now we draw the finalized buffer to the display canvas. You don't need to\r\n      use a buffer; you could draw your tiles directly to the display canvas. If\r\n      you are going to scale your display canvas at all, however, I recommend this\r\n      method, because it eliminates antialiasing problems that arize due to scaling\r\n      individual tiles. It is somewhat slower, however. */\r\n      this.context.drawImage(this.buffer.canvas, 0, 0, world.width, world.height, 0, 0, this.context.canvas.width, this.context.canvas.height);\r\n\r\n    },\r\n\r\n    /* Resizes the display canvas when the screen is resized. */\r\n    resize:function(event) {\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n      if (display.context.canvas.width > document.documentElement.clientHeight - 16) {\r\n\r\n        display.context.canvas.width = document.documentElement.clientHeight - 16;\r\n\r\n      }\r\n\r\n      /* That height_width_ratio comes into play here. */\r\n      display.context.canvas.height = display.context.canvas.width * display.height_width_ratio;\r\n\r\n      display.buffer.imageSmoothingEnabled = display.context.imageSmoothingEnabled = false;\r\n\r\n      display.render();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  /* The world holds information about the tile map. */\r\n  world = {\r\n\r\n    map: [7, 7, 8, 8, 8, 8, 8, 8,\r\n          0, 7, 7, 7, 7, 8, 8, 8,\r\n          1, 7, 7, 7, 7, 7, 7, 7,\r\n          2, 3, 7, 7, 6, 7, 6, 7,\r\n          4, 2, 5, 5, 5, 5, 5, 5],\r\n\r\n    columns:8,\r\n\r\n    height:80,\r\n    width:124\r\n\r\n  };\r\n\r\n  //// INITIALIZE ////\r\n\r\n  /* Before we can draw anything we have to load the tile_sheet image. */\r\n  display.tile_sheet.image.addEventListener(\"load\", function(event) {\r\n\r\n    display.buffer.canvas.height = world.height;\r\n    display.buffer.canvas.width  = world.width;\r\n    display.height_width_ratio   = world.height / world.width;\r\n\r\n    display.resize();\r\n\r\n  });\r\n\r\n  /* Start loading the image. */\r\n  display.tile_sheet.image.src = \"tile-graphics.png\";\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/tile-grid/tile-grid.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-content:center;\r\n  min-height:100%;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n\r\n}\r\n"
  },
  {
    "path": "content/tile-grid/tile-grid.html",
    "content": "<!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=device-width\">\r\n\r\n    <link href = \"tile-grid.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Tile Grid</h1>\r\n\r\n    <p style = \"font-size:1.25em\">output</p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"tile-grid.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/tile-grid/tile-grid.js",
    "content": "// 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  buffer = document.createElement(\"canvas\").getContext(\"2d\");\r\n  context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n  output = document.querySelector(\"p\");\r\n\r\n  size = 32;\r\n\r\n  buffer.canvas.width = 16 * size;\r\n  buffer.canvas.height = 9 * size;\r\n\r\n  map = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\r\n         1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n         1,0,1,1,1,0,0,1,0,0,1,0,0,1,0,1,\r\n         1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,\r\n         1,0,0,1,0,0,0,1,0,1,1,1,0,1,0,1,\r\n         1,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,\r\n         1,0,0,1,0,1,0,1,0,0,1,1,0,1,0,1,\r\n         1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];\r\n\r\n  controller = {\r\n\r\n    // mouse or finger position\r\n    pointer_x:0,\r\n    pointer_y:0,\r\n\r\n    move:function(event) {\r\n\r\n      // This will give us the location of our canvas element on screen\r\n      var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n      // store the position of the move event inside the pointer variables\r\n      controller.pointer_x = event.clientX - rectangle.left;\r\n      controller.pointer_y = event.clientY - rectangle.top;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  drawMap = function() {\r\n\r\n    for (let index = 0; index < map.length; index ++) {\r\n\r\n      buffer.fillStyle = (map[index] == 1)?\"#228b22\":\"#b0e0b6\";\r\n      buffer.fillRect((index % 16) * size, Math.floor(index/16) * size, size, size);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  loop = function(time_stamp) {\r\n\r\n    var tile_x, tile_y, value;\r\n\r\n    tile_x = Math.floor(controller.pointer_x / (context.canvas.width/16));\r\n    tile_y = Math.floor(controller.pointer_y / (context.canvas.height/9));\r\n    value = map[tile_y * 16 + tile_x];\r\n\r\n    drawMap();\r\n\r\n    buffer.fillStyle = \"rgba(128, 128, 128, 0.5)\";\r\n    buffer.fillRect(tile_x * size, tile_y * size, size, size);\r\n\r\n    context.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n    output.innerHTML = \"tile_x: \" + tile_x + \"<br>tile_y: \" + tile_y + \"<br>value: \" + value;\r\n\r\n    window.requestAnimationFrame(loop);\r\n\r\n  };\r\n\r\n  // just keeps the canvas element sized appropriately\r\n  resize = function(event) {\r\n\r\n    context.canvas.width = Math.floor(document.documentElement.clientWidth - 32);\r\n\r\n    if (context.canvas.width > document.documentElement.clientHeight) {\r\n\r\n      context.canvas.width = Math.floor(document.documentElement.clientHeight);\r\n\r\n    }\r\n\r\n    context.canvas.height = Math.floor(context.canvas.width * 0.5625);\r\n\r\n    drawMap();\r\n\r\n  };\r\n\r\n  window.addEventListener(\"resize\", resize, {passive:true});\r\n  context.canvas.addEventListener(\"mousemove\", controller.move);\r\n  context.canvas.addEventListener(\"touchmove\", controller.move, {passive:true});\r\n  context.canvas.addEventListener(\"touchstart\", controller.move, {passive:true});\r\n\r\n  resize();\r\n\r\n  window.requestAnimationFrame(loop);\r\n\r\n})();\r\n"
  },
  {
    "path": "content/tile-scroll/tile-scroll.html",
    "content": "<!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=device-width\">\r\n\r\n    <title>Tile Scroll</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; }\r\n\r\n      html, body { background-color:#000000; height:100%; position:relative; width:100%; }\r\n\r\n      canvas { height:100%; position:fixed; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      /* The player is just a simple 2d point with a moveTo function. */\r\n      const Player = function(x, y) {\r\n\r\n        this.x = x; this.y = y;\r\n\r\n      };\r\n\r\n      Player.prototype = {\r\n\r\n        moveTo:function(x, y) {\r\n\r\n          /* Gradually moves the player closer to x, y every time moveTo is called. */\r\n          this.x += (x - this.x - scaled_size * 0.5) * 0.05;\r\n          this.y += (y - this.y - scaled_size * 0.5) * 0.05;\r\n\r\n        }\r\n\r\n      };\r\n\r\n      /* The viewport (camera) is a rectangular region that defines the visible\r\n      area of the map to be drawn. It's x, y coordinates are relative to the map\r\n      itself, but you can easily draw its contents in a stationary location on screen,\r\n      thus giving the effect of scrolling. */\r\n      const Viewport = function(x, y, w, h) {\r\n\r\n        this.x = x; this.y = y; this.w = w; this.h = h;\r\n\r\n      };\r\n\r\n      Viewport.prototype = {\r\n\r\n        scrollTo:function(x, y) {\r\n\r\n          this.x = x - this.w * 0.5;// Rigid scrolling\r\n          this.y = y - this.w * 0.5;\r\n\r\n          // Smooth scrolling (forgot to put this in the video)\r\n          //this.x += (x - this.x - this.w * 0.5) * 0.05;\r\n          //this.y += (y - this.y - this.h * 0.5) * 0.05;\r\n\r\n        }\r\n\r\n      };\r\n\r\n      var scaled_size = 32;// The size I want my sprites to be;\r\n      var sprite_size = 16;// The actual size of sprites / tiles in the tile_sheet image\r\n      var columns   = 24;// columns and rows in map below\r\n      var rows      = 24;\r\n      var map = [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,\r\n                 3,2,1,1,0,0,3,3,3,2,1,0,3,0,0,0,3,0,0,1,2,2,2,3,\r\n                 3,1,1,0,0,0,3,3,3,1,0,0,3,0,2,0,3,0,1,1,2,1,1,3,\r\n                 3,0,0,0,0,0,3,3,2,0,0,0,3,0,0,0,3,1,2,2,2,1,1,3,\r\n                 3,1,1,0,0,0,3,1,1,0,0,0,3,3,3,0,1,1,2,2,1,0,0,3,\r\n                 3,0,0,1,2,1,3,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,3,\r\n                 3,0,1,2,2,1,3,0,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,3,\r\n                 3,0,0,1,1,1,3,1,1,1,0,1,0,0,0,3,0,0,3,3,3,0,0,3,\r\n                 3,0,0,0,1,1,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,\r\n                 3,3,0,3,3,3,3,3,3,3,3,1,0,0,0,3,0,0,3,3,3,2,1,3,\r\n                 3,3,1,0,0,1,3,3,3,3,3,0,0,0,0,0,0,1,1,0,1,1,0,3,\r\n                 3,3,3,3,1,1,3,3,3,3,3,1,0,0,0,0,1,1,2,2,1,0,0,3,\r\n                 3,3,3,3,0,1,0,0,3,3,1,0,0,1,1,2,1,2,0,1,2,1,0,3,\r\n                 3,2,3,0,0,0,1,0,1,1,0,0,1,0,0,2,1,2,2,1,2,1,1,3,\r\n                 3,1,1,1,0,0,0,0,1,1,0,0,0,1,1,0,2,1,1,1,2,0,1,3,\r\n                 3,1,1,1,1,1,0,1,3,3,1,0,0,0,1,1,1,2,2,2,1,1,2,3,\r\n                 3,0,0,0,1,0,1,1,3,3,1,0,0,0,0,1,0,1,1,1,1,1,1,3,\r\n                 3,1,1,0,0,0,0,3,3,3,1,1,2,2,0,0,3,3,3,3,3,3,3,3,\r\n                 3,0,1,0,1,0,1,3,3,3,3,2,2,2,2,1,3,1,0,0,0,0,1,3,\r\n                 3,1,0,0,0,1,3,3,3,2,1,0,1,2,0,1,0,0,0,1,1,0,0,3,\r\n                 3,2,0,0,0,0,3,3,3,3,1,1,0,1,1,0,3,0,1,2,2,1,0,3,\r\n                 3,3,1,0,1,1,3,3,3,3,3,3,0,0,1,1,3,0,0,1,1,0,0,3,\r\n                 3,3,1,1,2,3,3,3,3,3,3,3,1,0,1,2,3,1,0,0,0,0,1,3,\r\n                 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3];\r\n\r\n      /* The drawing context of the on screen canvas */\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      /* The width and height of the inside of the browser window */\r\n      var height = document.documentElement.clientHeight;\r\n      var width  = document.documentElement.clientWidth;\r\n\r\n      var player = new Player(100, 100);\r\n      var viewport = new Viewport(200, 200, 300, 300);\r\n\r\n      var pointer = { x:0, y:0 };// The adjusted mouse position\r\n\r\n      function loop() {// The game loop\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n          var height = document.documentElement.clientHeight;\r\n          var width  = document.documentElement.clientWidth;\r\n\r\n          /* Resize canvas on every frame */\r\n          context.canvas.height = height;\r\n          context.canvas.width  = width;\r\n\r\n          context.imageSmoothingEnabled = false;// prevent antialiasing of drawn image\r\n\r\n          player.moveTo(pointer.x, pointer.y);\r\n          viewport.scrollTo(player.x, player.y);\r\n\r\n          /* Get the min and max column and row in the map to draw. For the min\r\n          column and row (x and y) we use floor to round down and for the max we\r\n          use ceil to round up. We want to get the rows and columns under the borders\r\n          of the viewport rectangle. This is visualized by the white square in the example. */\r\n          var x_min = Math.floor(viewport.x / scaled_size);\r\n          var y_min = Math.floor(viewport.y / scaled_size);\r\n          var x_max = Math.ceil((viewport.x + viewport.w) / scaled_size);\r\n          var y_max = Math.ceil((viewport.y + viewport.h) / scaled_size);\r\n\r\n          /* the min and max column and row values cannot go beyond the boundaries\r\n          of the map. Those values are 0 and the number of columns and rows in the map. */\r\n          if (x_min < 0) x_min = 0;\r\n          if (y_min < 0) y_min = 0;\r\n          if (x_max > columns) x_max = columns;\r\n          if (y_max > rows) y_max = rows;\r\n\r\n          /* Now we loop through the tiles in the map, but only between the min\r\n          and max columns and rows that the viewport is over. To do this we use two\r\n          for loops, one for the columns (x) and one for the rows (y) of the map. */\r\n          for (let x = x_min; x < x_max; x ++) {\r\n\r\n            for (let y = y_min; y < y_max; y ++) {\r\n\r\n              let value = map[y * columns + x];// Tile value\r\n              let tile_x = Math.floor(x * scaled_size - viewport.x + width * 0.5 - viewport.w * 0.5);// Tile x destination for drawing\r\n              let tile_y = Math.floor(y * scaled_size - viewport.y + height * 0.5 - viewport.h * 0.5);// Tile y destination for drawing\r\n\r\n              // Draw tile from tile_sheet\r\n              context.drawImage(tile_sheet, value * sprite_size, 0, sprite_size, sprite_size, tile_x, tile_y, scaled_size, scaled_size);\r\n\r\n            }\r\n\r\n          }\r\n\r\n          /* This bit of code gets the player's position in the world in terms of\r\n          columns and rows and converts it to an index in the map array */\r\n          let player_index = Math.floor((player.y + scaled_size * 0.5) / scaled_size) * columns + Math.floor((player.x + scaled_size * 0.5) / scaled_size);\r\n\r\n          /* If the player is standing on a grass tile, make it a short grass tile */\r\n          if (map[player_index] == 2) map[player_index] = 1;\r\n\r\n          /* Draw the player. Remember to offset by the viewport position and\r\n          center screen position. */\r\n          context.drawImage(tile_sheet, 64, 0, sprite_size, sprite_size, Math.round(player.x - viewport.x + width * 0.5 - viewport.w * 0.5), Math.round(player.y - viewport.y + height * 0.5 - viewport.h * 0.5), scaled_size, scaled_size);\r\n\r\n          /* Draw the viewport rectangle. */\r\n          context.strokeStyle = \"#ffffff\";\r\n          context.rect(width * 0.5 - viewport.w * 0.5, height * 0.5 - viewport.h * 0.5, viewport.w, viewport.h);\r\n          context.stroke();\r\n\r\n      }\r\n\r\n      var tile_sheet = new Image();\r\n\r\n      tile_sheet.addEventListener(\"load\", (event) => { loop(); });\r\n\r\n      tile_sheet.src = \"tile-scroll.png\";\r\n\r\n      context.canvas.addEventListener(\"click\", (event) => {\r\n\r\n        pointer.x = event.pageX + viewport.x - width * 0.5 + viewport.w * 0.5;\r\n        pointer.y = event.pageY + viewport.y - height * 0.5 + viewport.h * 0.5;\r\n\r\n      });\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/tile-types/tile-types.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  overflow:hidden;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  align-self:center;\r\n  background-color:#303840;\r\n  display:grid;\r\n  justify-self:center;\r\n\r\n}\r\n\r\np {\r\n\r\n  font-family:monospace;\r\n  font-weight:800;\r\n  font-size:1.2em;\r\n\r\n}\r\n"
  },
  {
    "path": "content/tile-types/tile-types.html",
    "content": "<!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    <meta name = \"description\" content = \"An open source example of tile based collision detection and response including these collision shapes: half tiles, sloped tiles, platform tiles, curved tiles, and regular tiles.\">\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n    <title>PoP Vlog - Tile Types</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Tile Types</h1>\r\n\r\n    <p></p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>The purpose of this example is to showcase some different types of tile based collision shapes. Use the keyboard to move the <span style = \"color:#ff9900\">yellow</span> rectangle around the level and interact with each shape.</p>\r\n\r\n    <img style = \"display:none\" src = \"tile-types.png\">\r\n\r\n    <script src = \"tile-types.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/tile-types/tile-types.js",
    "content": "// 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    //// OBJECTS ////\r\n  /////////////////\r\n\r\n  var controller, display, game;\r\n\r\n  controller = {\r\n\r\n    down:false, left:false, right:false, up:false,\r\n\r\n    // A very simple key up/down event handler:\r\n    keyUpDown:function(event) {\r\n\r\n      var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left = key_state; break;// left key\r\n        case 38: controller.up = key_state; break;// up key\r\n        case 39: controller.right = key_state; break;// right key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    output:document.querySelector(\"p\"),// The output p element.\r\n    tile_sheet: document.querySelector(\"img\"),// The tile sheet graphic.\r\n\r\n    render:function(game) {\r\n\r\n      /* Loop through the map and draw all the tiles. */\r\n      for (let index = game.area.map.length - 1; index > -1; -- index) {\r\n\r\n        this.buffer.drawImage(this.tile_sheet, game.area.map[index] * TILE_SIZE, 0, TILE_SIZE, TILE_SIZE, (index % game.area.columns) * TILE_SIZE, Math.floor(index / game.area.columns) * TILE_SIZE, TILE_SIZE, TILE_SIZE);\r\n\r\n      }\r\n\r\n      /* Draw the player character. */\r\n      this.buffer.drawImage(this.tile_sheet, 240, 0, TILE_SIZE, TILE_SIZE, game.player.x, game.player.y, game.player.width, game.player.height);\r\n\r\n      /* Draw the buffer to the canvas. This takes care of scaling. */\r\n      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);\r\n\r\n    },\r\n\r\n    /* Resizes the display canvas when the window is resized. */\r\n    resize:function(event) {\r\n\r\n      let height = document.documentElement.clientHeight;\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 16;\r\n\r\n      if (display.context.canvas.width >= height * 0.5) {\r\n\r\n        display.context.canvas.width = height * 0.5;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = display.context.canvas.width;\r\n\r\n      display.render(game);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  game = {\r\n\r\n    /* The area object holds information about the map. */\r\n    area: {\r\n\r\n      columns:8,\r\n      map:[ 0, 0, 0, 0, 0, 0, 3, 0,\r\n            0, 0, 5, 0, 0, 0, 0, 4,\r\n            0, 7, 0, 0, 6, 2, 0, 4,\r\n            0, 1, 1, 1, 1, 1, 1, 1,\r\n            7, 0, 0, 0, 0, 0,13, 0,\r\n            0,11,12, 9,10, 0, 0, 7,\r\n            0, 1, 1, 1, 1, 1, 1, 1,\r\n           12, 0,14,14,14, 0, 8, 0]\r\n    },\r\n\r\n    player: {\r\n\r\n      jumping:   true,\r\n      height:    TILE_SIZE - 4,\r\n      width:     TILE_SIZE - 4,\r\n      x:         TILE_SIZE * 4 - TILE_SIZE * 0.5 + 2,\r\n      x_old:     TILE_SIZE * 4 - TILE_SIZE * 0.5 + 2,\r\n      x_velocity:0,\r\n      y:         TILE_SIZE * 4,\r\n      y_old:     TILE_SIZE * 8,\r\n      y_velocity:0,\r\n\r\n    },\r\n\r\n    collider: {\r\n\r\n      offset:0.001,\r\n\r\n      /* A platform tile with a flat top surface. */\r\n      1:function(object, column, row) {\r\n\r\n        this.collideTop(object, row);\r\n\r\n      },\r\n\r\n      /* A platform tile with a flat right surface. */\r\n      2:function(object, column, row) {\r\n\r\n        this.collideRight(object, column);\r\n\r\n      },\r\n\r\n      /* A platform tile with a flat bottom surface. */\r\n      3:function(object, column, row) {\r\n\r\n        this.collideBottom(object, row);\r\n\r\n      },\r\n\r\n      /* A platform tile with a flat left surface. */\r\n      4:function(object, column, row) {\r\n\r\n        this.collideLeft(object, column);\r\n\r\n      },\r\n\r\n      /* A platform tile with flat top, right, bottom, and left surfaces. */\r\n      5:function(object, column, row) {\r\n\r\n        if (this.collideTop(object, row)) return;\r\n        if (this.collideLeft(object, column)) return;\r\n        if (this.collideRight(object, column)) return;\r\n        this.collideBottom(object, row);\r\n\r\n      },\r\n\r\n      /* A half height platform tile residing in the bottom half of the tile space\r\n      with flat top, right, bottom, and left surfaces. */\r\n      6:function(object, column, row) {\r\n\r\n        if (this.collideTop(object, row, TILE_SIZE * 0.5)) return;\r\n\r\n        if (object.y + object.height > row * TILE_SIZE + TILE_SIZE * 0.5) {\r\n\r\n          if (this.collideLeft(object, column)) return;\r\n          if (this.collideRight(object, column)) return;\r\n\r\n        }\r\n\r\n        this.collideBottom(object, row);\r\n\r\n      },\r\n\r\n      /* A half height platform tile residing in the bottom half of the tile space\r\n      with a flat top surface. */\r\n      7:function(object, column, row) {\r\n\r\n        this.collideTop(object, row, TILE_SIZE * 0.5);\r\n\r\n      },\r\n\r\n      /* A slope tile starting in the bottom left corner and rising to the top right\r\n      corner of the tile space that only pushes objects up out of it. This is not\r\n      a platform tile, it is a solid tile. */\r\n      8:function(object, column, row) {\r\n\r\n        let current_x = object.x + object.width - column * TILE_SIZE;\r\n\r\n        let top = -1 * current_x + TILE_SIZE + row * TILE_SIZE;\r\n\r\n        if (current_x > TILE_SIZE) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = row * TILE_SIZE - object.height - this.offset;\r\n\r\n        } else if (object.y + object.height > top) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = top - object.height - this.offset;\r\n\r\n        }\r\n\r\n      },\r\n\r\n      /* A slope tile starting in the bottom left corner and rising to the top right\r\n      corner of the tile space. */\r\n      9:function(object, column, row) {\r\n\r\n        this.collideSlopeTop(object, column, row, -1, TILE_SIZE);\r\n\r\n      },\r\n\r\n      /* A slope tile starting in the top left corner and declining to the bottom\r\n      right corner of the tile space. */\r\n      10:function(object, column, row) {\r\n\r\n        this.collideSlopeTop(object, column, row, 1, 0);\r\n\r\n      },\r\n\r\n      /* A half height slope tile starting in the bottom left corner and rising to\r\n      the middle right side of the tile space. */\r\n      11:function(object, column, row) {\r\n\r\n        this.collideSlopeTop(object, column, row, -0.5, TILE_SIZE);\r\n\r\n      },\r\n\r\n      /* A half height slope tile starting in the middle left side and declining\r\n      to the bottom right corner of the tile space. */\r\n      12:function(object, column, row) {\r\n\r\n        this.collideSlopeTop(object, column, row, 0.5, TILE_SIZE * 0.5);\r\n\r\n      },\r\n\r\n      /* A slope tile starting in the top left corner and declining to the bottom\r\n      right corner of the tile space, with flat surfaces on the bottom and left sides. */\r\n      13:function(object, column, row) {\r\n\r\n        if (this.collideSlopeTop(object, column, row, 1, 0)) return;\r\n        if (this.collideLeft(object, column)) return;\r\n\r\n        let bottom = row * TILE_SIZE + TILE_SIZE;\r\n        let right = column * TILE_SIZE + TILE_SIZE;\r\n\r\n        if (object.x < right && object.x_old >= right && object.y < bottom && object.y + object.height > bottom) {\r\n\r\n          object.x_velocity = 0;\r\n          object.x = right;\r\n\r\n        }\r\n\r\n        this.collideBottom(object, row);\r\n\r\n      },\r\n\r\n      /* A half height curve tile that simply pushes the object up when collision is detected. */\r\n      14:function(object, column, row) {\r\n\r\n        /* x is the x center of the object adjusted so that x = 0 when the object\r\n        is in the center of the tile space. */\r\n        let x = (object.x + object.width * 0.5) - (column * TILE_SIZE + TILE_SIZE * 0.5);\r\n        /* y_vertex is the y value at the peak of the curve or the turn around point,\r\n        or whatever you'd like to call it. In this case it's just at the top of our\r\n        curve tile. */\r\n        let y_vertex = row * TILE_SIZE + TILE_SIZE * 0.5;\r\n        /* The coefficient determines how wide the curve will be at its base or\r\n        to be exact, how wide it will be at the y intercept. This particular formula\r\n        will yield a width of the full tile size at the base of the tile should\r\n        the vertex be in the center of the tile space. Changing the 2 can yield different\r\n        heights. For instance, 4 would give the width of the base if the vertex were at\r\n        the top of the tile space and the base were at the bottom. I haven't tested\r\n        this with a TILE_SIZE other than 16, so this formula may be garbage. */\r\n        let coefficient = TILE_SIZE / ((TILE_SIZE * TILE_SIZE) / 2);\r\n\r\n        /* This is the basic formula for a quadratic curve: y = a(x - h)^2 + k\r\n        Where h is the x offset, and k is the y_vertex */\r\n        let top = coefficient * x * x + y_vertex;\r\n\r\n        if (object.y + object.height > top) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = top - object.height - this.offset;\r\n\r\n        }\r\n\r\n      },\r\n\r\n      collideBottom:function(object, row, y_offset = TILE_SIZE) {\r\n\r\n        let bottom = row * TILE_SIZE + y_offset;\r\n\r\n        if (object.y < bottom && object.y_old >= bottom) {\r\n\r\n          object.y_velocity = 0;\r\n          object.y = bottom + this.offset;\r\n\r\n          return true;\r\n\r\n        } return false;\r\n\r\n      },\r\n\r\n      collideLeft:function(object, column) {\r\n\r\n        let left = column * TILE_SIZE;\r\n\r\n        if (object.x + object.width > left && object.x_old + object.width <= left) {\r\n\r\n          object.x_velocity = 0;\r\n          object.x = left - object.width - this.offset;\r\n\r\n          return true;\r\n\r\n        } return false;\r\n\r\n      },\r\n\r\n      collideRight:function(object, column) {\r\n\r\n        let right = column * TILE_SIZE + TILE_SIZE;\r\n\r\n        if (object.x < right && object.x_old >= right) {\r\n\r\n          object.x_velocity = 0;\r\n          object.x = right;\r\n\r\n          return true;\r\n\r\n        } return false;\r\n\r\n      },\r\n\r\n      collideTop:function(object, row, y_offset = 0) {\r\n\r\n        let top = row * TILE_SIZE + y_offset;\r\n\r\n        if (object.y + object.height > top && object.y_old + object.height <= top) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = top - object.height - this.offset;\r\n\r\n          return true;\r\n\r\n        } return false;\r\n\r\n      },\r\n\r\n      /* This function handles collision with slope tiles on the y axis. */\r\n      collideSlopeTop:function(object, column, row, slope, y_offset) {\r\n\r\n        let origin_x = column * TILE_SIZE;\r\n        let origin_y = row * TILE_SIZE + y_offset;\r\n        let current_x = (slope < 0) ? object.x + object.width - origin_x : object.x - origin_x;\r\n        let current_y = object.y + object.height - origin_y;\r\n        let old_x = (slope < 0) ? object.x_old + object.width - origin_x : object.x_old - origin_x;\r\n        let old_y = object.y_old + object.height - origin_y;\r\n        let current_cross_product = current_x * slope - current_y;\r\n        let old_cross_product     = old_x * slope - old_y;\r\n        let top = (slope < 0) ? row * TILE_SIZE + TILE_SIZE + y_offset * slope : row * TILE_SIZE + y_offset;\r\n\r\n        if ((current_x < 0 || current_x > TILE_SIZE) && (object.y + object.height > top && object.y_old + object.height <= top || current_cross_product < 1 && old_cross_product > -1)) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = top - object.height - this.offset;\r\n\r\n          return true;\r\n\r\n        } else if (current_cross_product < 1 && old_cross_product > -1) {\r\n\r\n          object.jumping = false;\r\n          object.y_velocity = 0;\r\n          object.y = row * TILE_SIZE + slope * current_x + y_offset - object.height - this.offset;\r\n\r\n          return true;\r\n\r\n        } return false;\r\n\r\n      },\r\n\r\n      handleCollision:function(object, area) {\r\n\r\n        var column, row, value;\r\n\r\n        /* TEST TOP */\r\n\r\n        column = Math.floor(object.x / TILE_SIZE);// The column under the left side of the object:\r\n        row    = Math.floor(object.y / TILE_SIZE);// The row under the top side of the object:\r\n        value  = area.map[row * area.columns + column];// We get the tile value under the top left corner of the object:\r\n\r\n        if (value != 0) this[value](object, column, row);// If it's not a walkable tile, we do narrow phase collision.\r\n\r\n        column = Math.floor((object.x + object.width) / TILE_SIZE);// The column under the right side of the object:\r\n        value  = area.map[row * area.columns + column];// Value under the top right corner of the object.\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        /* TEST BOTTOM */\r\n\r\n        column = Math.floor(object.x / TILE_SIZE);// The column under the left side of the object:\r\n        row    = Math.floor((object.y + object.height) / TILE_SIZE);// The row under the bottom side of the object:\r\n        value  = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        column = Math.floor((object.x + object.width) / TILE_SIZE);// The column under the right side of the object:\r\n        value  = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        /* TEST LEFT */\r\n\r\n        column = Math.floor(object.x / TILE_SIZE);// The column under the left side of the object:\r\n        row    = Math.floor(object.y / TILE_SIZE);// Top side row:\r\n        value  = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        row = Math.floor((object.y + object.height) / TILE_SIZE);// Bottom side row:\r\n        value = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        /* TEST RIGHT */\r\n\r\n        column = Math.floor((object.x + object.width) / TILE_SIZE);// The column under the right side of the object:\r\n        row    = Math.floor(object.y / TILE_SIZE);// Top side row:\r\n        value  = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n        row = Math.floor((object.y + object.height) / TILE_SIZE);// Bottom side row:\r\n        value = area.map[row * area.columns + column];\r\n\r\n        if (value != 0) this[value](object, column, row);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    loop: function(time_stamp) {\r\n\r\n      if (controller.up && !game.player.jumping) {\r\n\r\n        game.player.jumping = true;\r\n        game.player.y_velocity = -10;\r\n\r\n      }\r\n\r\n      if (controller.left) {\r\n\r\n        game.player.x_velocity -= 0.2;\r\n\r\n      }\r\n\r\n      if (controller.right) {\r\n\r\n        game.player.x_velocity += 0.2;\r\n\r\n      }\r\n\r\n      game.player.x_old = game.player.x;\r\n      game.player.y_old = game.player.y;\r\n\r\n      game.player.y_velocity += 1;\r\n\r\n      game.player.x += game.player.x_velocity;\r\n      game.player.y += game.player.y_velocity;\r\n\r\n      game.player.x_velocity *= 0.9;\r\n      game.player.y_velocity *= 0.9;\r\n\r\n      // Collision detection and response handling with the walls:\r\n\r\n      if (game.player.y < 0) {\r\n\r\n        game.player.y_velocity = 0;\r\n        game.player.y = 0;\r\n\r\n      } else if (game.player.y + game.player.height > display.buffer.canvas.height) {\r\n\r\n        game.player.jumping = false;\r\n        game.player.y_velocity = 0;\r\n        game.player.y = display.buffer.canvas.height - game.player.height - 0.001;\r\n\r\n      }\r\n\r\n      if (game.player.x < 0) {\r\n\r\n        game.player.x_velocity = 0;\r\n        game.player.x = 0;\r\n\r\n      } else if (game.player.x + game.player.width > display.buffer.canvas.width) {\r\n\r\n        game.player.x_velocity = 0;\r\n        game.player.x = display.buffer.canvas.width - game.player.width - 0.001;\r\n\r\n      }\r\n\r\n      // Handle collision with world AFTER moving the player.\r\n      game.collider.handleCollision(game.player, game.area);\r\n\r\n      display.render(game);\r\n\r\n      display.output.innerHTML = \"player bottom: \" + (game.player.y + game.player.height) + \"<br>tile top:      \" + Math.round(game.player.y + game.player.height) + \"<br>tile row:      \" + (Math.floor((game.player.y + game.player.height) / TILE_SIZE));\r\n\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    }\r\n\r\n  };\r\n\r\n      ////////////////////\r\n    //// INITIALIZE ////\r\n  ////////////////////\r\n\r\n  display.buffer.canvas.height = 128;\r\n  display.buffer.canvas.width = 128;\r\n  display.output.style.textAlign = \"left\";\r\n  display.output.style.justifySelf = \"center\";\r\n  display.output.style.whiteSpace = \"pre\";\r\n\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n  window.addEventListener(\"resize\", display.resize);\r\n\r\n  display.resize();\r\n\r\n  game.loop();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/tile-world/tile-world.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n\r\n}\r\n"
  },
  {
    "path": "content/tile-world/tile-world.html",
    "content": "<!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 = \"width=device-width\">\r\n\r\n    <link href = \"tile-world.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Tile World</h1>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"tile-world.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/tile-world/tile-world.js",
    "content": "// 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.createElement(\"canvas\").getContext(\"2d\");\r\n  context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n  map = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\r\n         1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n         1,0,1,1,1,0,0,1,0,0,1,0,0,1,0,1,\r\n         1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,\r\n         1,0,0,1,0,0,0,1,0,1,1,1,0,1,0,1,\r\n         1,0,0,1,0,1,0,1,0,1,0,0,0,0,0,1,\r\n         1,0,0,1,0,1,0,1,0,0,1,1,0,1,0,1,\r\n         1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];\r\n\r\n  size = 32;\r\n\r\n  buffer.canvas.width = 16 * size;\r\n  buffer.canvas.height = 9 * size;\r\n\r\n  drawMap = function() {\r\n\r\n    for (let index = 0; index < map.length; index ++) {\r\n\r\n      buffer.fillStyle = (map[index] == 1)?\"#000000\":\"#ffffff\";\r\n      buffer.fillRect((index % 16) * size, Math.floor(index/16) * size, size, size);\r\n\r\n    }\r\n\r\n    context.drawImage(buffer.canvas, 0, 0, buffer.canvas.width, buffer.canvas.height, 0, 0, context.canvas.width, context.canvas.height);\r\n\r\n  };\r\n\r\n  // just keeps the canvas element sized appropriately\r\n  resize = function(event) {\r\n\r\n    context.canvas.width = Math.floor(document.documentElement.clientWidth - 32);\r\n\r\n    if (context.canvas.width > document.documentElement.clientHeight) {\r\n\r\n      context.canvas.width = Math.floor(document.documentElement.clientHeight);\r\n\r\n    }\r\n\r\n    context.canvas.height = Math.floor(context.canvas.width * 0.5625);\r\n\r\n    drawMap();\r\n\r\n  };\r\n\r\n  window.addEventListener(\"resize\", resize, {passive:true});\r\n\r\n  resize();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#303840;\r\n  justify-self:center;\r\n\r\n}\r\n"
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.html",
    "content": "<!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 = \"description\" content = \"PoP Vlog 21 - A top down tile based collision detection and response example in Javascript.\">\r\n    <link href = \"top-down-tiles.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n    <title>PoP Vlog - Top Down Tiles</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Top Down Tiles</h1>\r\n\r\n    <p>&nbsp;</p> <!-- the output p element -->\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>This example requires a keyboard.</p>\r\n\r\n    <script src = \"top-down-tiles.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/top-down-tiles/top-down-tiles.js",
    "content": "// 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(function() { \"use strict\"\r\n\r\n  var controller, display, game;\r\n\r\n  controller = {\r\n\r\n    down:false, left:false, right:false, up:false,\r\n\r\n    keyUpDown:function(event) {\r\n\r\n      var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left = key_state; break; // left key\r\n        case 38: controller.up = key_state; break; // up key\r\n        case 39: controller.right = key_state; break; // right key\r\n        case 40: controller.down = key_state; break; // down key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    output:document.querySelector(\"p\"),\r\n\r\n    render:function() {\r\n\r\n      for (let index = game.world.map.length - 1; index > -1; -- index) {\r\n\r\n        this.buffer.fillStyle = (game.world.map[index] > 0)?(\"#0099\" + game.world.map[index] + \"f\"):\"#303840\";\r\n        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);\r\n\r\n      }\r\n\r\n      this.buffer.fillStyle = game.player.color;\r\n      this.buffer.fillRect(game.player.x, game.player.y, game.player.width, game.player.height);\r\n\r\n      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);\r\n\r\n    },\r\n\r\n    resize:function(event) {\r\n\r\n      var client_height = document.documentElement.clientHeight;\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n      if (display.context.canvas.width > client_height) {\r\n\r\n        display.context.canvas.width = client_height;\r\n\r\n      }\r\n\r\n      display.context.canvas.height = Math.floor(display.context.canvas.width * 0.625);\r\n\r\n      display.render();\r\n\r\n    }\r\n\r\n  };\r\n\r\n  game = {\r\n\r\n    /* This is the player object. Make sure to note that for this collision method\r\n    to work, you must record the current and last positions of your game objects\r\n    to use in collision detection. It is used to calculate the vector used to determine\r\n    if an object is entering into a collision tile and from what side. */\r\n    player: {\r\n\r\n      color:\"#ff9900\",\r\n      height:32,\r\n      old_x:160,// these are what you should take note of. Don't worry, it's useful\r\n      old_y:160,// to keep track of old positions for many physics methods. These aren't one trick pony's.\r\n      velocity_x:0,\r\n      velocity_y:0,\r\n      width:32,\r\n      x:160 - 16,\r\n      y:100 - 16,\r\n\r\n      // These functions just make it easy to read the collision code\r\n      get bottom()    { return this.y + this.height; },\r\n      get oldBottom() { return this.old_y + this.height; },\r\n      get left()      { return this.x; },// kind of pointless, but used\r\n      get oldLeft()   { return this.old_x; },// to help visualize the collision methods\r\n      get right()     { return this.x + this.width; },\r\n      get oldRight()  { return this.old_x + this.width; },\r\n      get top()       { return this.y; },// equally pointless as left side calculations\r\n      get oldTop()    { return this.old_y; }\r\n\r\n    },\r\n\r\n    world: {\r\n\r\n      columns:8,\r\n      rows:5,\r\n      tile_size:40,\r\n\r\n      map:[1,1,1,1,1,1,1,1,// 1s are ceiling tiles\r\n           2,0,0,0,0,0,0,3,// 2s and 3s are left and right wall tiles\r\n           2,0,5,0,0,5,0,3,// 5 is a solid block/box tile\r\n           2,0,0,0,0,0,0,3,// 4 is a floor tile\r\n           4,4,4,4,4,4,4,4]// the routing functions below with matching numbers\r\n                           // represent these tiles and route their values to\r\n                           // the collision methods you might expect based on their names\r\n\r\n    },\r\n\r\n    /* This object is responsible for getting the collision methods that match\r\n    tile values in the map. It uses what I call routing functions to group reusable\r\n    narrow phase collision methods together to create a variety of different tile\r\n    boundary shapes. You can get the same effect with an array of routing functions rather\r\n    than giving an object's routing functions numeric indexes, if you prefer. It might be faster. */\r\n    collision: {\r\n\r\n      // top wall collision tile\r\n      1:function(object, row, column) {\r\n\r\n        // The player hits the bottom of ceiling tiles.\r\n        this.bottomCollision(object, row);\r\n\r\n      },\r\n\r\n      // left wall collision tile\r\n      2:function(object, row, column) {\r\n\r\n        this.rightCollision(object, column);\r\n\r\n      },\r\n\r\n      // right wall collision tile\r\n      3:function(object, row, column) {\r\n\r\n        // The player collides with the left side of a wall on the right of the map.\r\n        this.leftCollision(object, column);// Confusing to visualize, but true.\r\n\r\n      },\r\n\r\n      // bottom wall collision tile\r\n      4:function(object, row, column) {\r\n\r\n        this.topCollision(object, row);\r\n\r\n      },\r\n\r\n      // block tile with four walls\r\n      5:function(object, row, column) {\r\n\r\n        /* It makes sense to test the most likely side to be collided with first.\r\n        In a top down game, all sides are hit about the same number of times, depending\r\n        on gameplay, but in a side scroller with downward pulling gravity, the tops of\r\n        tiles will usually get the most traffic, so it makes sense to test the tops\r\n        first in those scenarios. */\r\n        if (this.topCollision(object, row)) { return; }// Make sure to early out\r\n        if (this.leftCollision(object, column)) { return; }// if a collision is detected.\r\n        if (this.rightCollision(object, column)) { return; }\r\n        this.bottomCollision(object, row);// No need to early out on the last check.\r\n\r\n      },\r\n\r\n      /* Here are the narrow phase collision detection and response functions.\r\n      For a deeper insight into how these work, and what's going on, I'm using\r\n      leftCollision as an example. The principles I explain here apply to all\r\n      of the other narrow phase collision methods. Note that they're basically\r\n      doing the same thing, just for different flat sides of a tile. */\r\n      leftCollision(object, column) {\r\n\r\n        /* To calculate the player's movement vector, I'm no longer using its velocity (like in the last tutorial).\r\n        Instead, I'm going to calculate it based on it's current and last positions.\r\n        the old way was if (object.velocity_x > 0), now it's if (object.x - object.old_x > 0).\r\n        This is a bit more reliable in my opinion, depending on how and when you calculate your velocity. */\r\n\r\n        /* If the object is not moving right, how will it ENTER into the left side of a tile?\r\n        If not moving right, the player can't possibly ENTER the left side of a tile.\r\n        We are only concerned with resolving collisions objects ENTER into. If they are spawned\r\n        inside a collision tile, it is not the collision manager's job to resolve that, it is\r\n        the level designer's job to fix the level design so collision can be as efficient as possible. */\r\n        if (object.x - object.old_x > 0) {\r\n\r\n          // the left side of the specified tile column\r\n          var left = column * game.world.tile_size;\r\n\r\n          /* This tests to see if our object is ENTERING through the collision boundary\r\n          along the correct movement vector. If its current position is past the boundary\r\n          and its old position is before the boundary, we know that it has entered into collision with the tile. */\r\n          if (object.right > left && object.oldRight <= left) {\r\n\r\n            object.velocity_x = 0;\r\n            object.x = object.old_x = left - object.width - 0.001;\r\n            /* You really don't need to reset the player's old position here if you are\r\n            setting it at the start of each game loop, but if you are using a fixed time step\r\n            game loop with interpolation, it will make your collisions look more pixel perfect.\r\n            The reason for this is because with interpolation, your player may be drawn slightly\r\n            away from the wall he just collided with on the next frame of animation depending on\r\n            how much time has passed. So, you don't need to do this, but it doesn't hurt. It may\r\n            be a problem if you end up changing old positions for player to player collisions as well,\r\n            however, because old positions are used to calculate vectors for tile collision.\r\n            Just make sure changing old positions are the last thing you do before the next frame,\r\n            and you should be fine. */\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      },\r\n\r\n      rightCollision(object, column) {\r\n\r\n        if (object.x - object.old_x < 0) {\r\n\r\n          // the right side of the specified tile column\r\n          var right = (column + 1) * game.world.tile_size;\r\n\r\n          if (object.left < right && object.oldLeft >= right) {\r\n\r\n            object.velocity_x = 0;\r\n            object.old_x = object.x = right;\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      },\r\n\r\n      bottomCollision(object, row) {\r\n\r\n        // if the object is moving up\r\n        if (object.y - object.old_y < 0) {\r\n\r\n          var bottom = (row + 1) * game.world.tile_size;\r\n\r\n          if (object.top < bottom && object.oldTop >= bottom) {\r\n\r\n            object.velocity_y = 0;\r\n            object.old_y = object.y = bottom;\r\n\r\n          }\r\n\r\n        }\r\n\r\n      },\r\n\r\n      topCollision(object, row) {\r\n\r\n        // if the object is moving down\r\n        if (object.y - object.old_y > 0) {\r\n\r\n          // the top side of the specified tile row\r\n          var top = row * game.world.tile_size;\r\n\r\n          // if the object has passed through the tile boundary since the last game cycle\r\n          if (object.bottom > top && object.oldBottom <= top) {\r\n\r\n            object.velocity_y = 0;\r\n            object.old_y = object.y = top - object.height - 0.01;\r\n\r\n            return true;\r\n\r\n          }\r\n\r\n        }\r\n\r\n        return false;\r\n\r\n      }\r\n\r\n    },\r\n\r\n    // The game loop:\r\n    loop:function() {\r\n\r\n      // Get controller input and move that player object!\r\n      if (controller.down) { game.player.velocity_y += 0.25; }\r\n\r\n      if (controller.left) { game.player.velocity_x -= 0.25; }\r\n\r\n      if (controller.right) { game.player.velocity_x += 0.25; }\r\n\r\n      if (controller.up) { game.player.velocity_y -= 0.25; }\r\n\r\n      // Update the player object:\r\n      game.player.old_x = game.player.x;// Set the old position to the current position\r\n      game.player.old_y = game.player.y;// before we update the current position, thus making it current\r\n\r\n      game.player.x += game.player.velocity_x;// Update the current position\r\n      game.player.y += game.player.velocity_y;\r\n\r\n      // Do collision detection and response with the boundaries of the screen.\r\n      if (game.player.x < 0) {\r\n\r\n        game.player.velocity_x = 0;\r\n        game.player.old_x = game.player.x = 0;\r\n\r\n      } else if (game.player.x + game.player.width > display.buffer.canvas.width) {\r\n\r\n        game.player.velocity_x = 0;\r\n        game.player.old_x = game.player.x = display.buffer.canvas.width - game.player.width - 0.001;\r\n        /* I added that - 0.001 to the equation to really push the player object back into\r\n        the game world. If the edge of the world is 320 px, and you set the right edge of\r\n        the player obect to 320, technically its tile position will be 1 tile to the right of\r\n        the edge of the world when calculated. Say there are 10 columns in the map, and each\r\n        tile is 32 pixels wide. 320/32 is 10, but since our map index starts at 0, a value of 10\r\n        falls outside of the map's number of columns. Failing to handle this can result in\r\n        testing collision on tiles in another row, or tiles at undefined positions in the map array.*/\r\n\r\n      }\r\n\r\n      if (game.player.y < 0) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.old_y = game.player.y = 0;\r\n\r\n      } else if (game.player.y + game.player.height > display.buffer.canvas.height) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.old_y = game.player.y = display.buffer.canvas.height - game.player.height - 0.001;\r\n\r\n      }\r\n\r\n      /* Here is where we do broadphase collision detection with the four corners of\r\n      our player object. This is very important. In the last tutorial the player only\r\n      had 1 collision registration point. Collision was simple, then. Now, it's just as\r\n      simple, but we need to do it for each corner of our player object. */\r\n\r\n      /* Once again, there's no point of testing collision if we're not moving. depending\r\n      on which direction we are moving we must test different collision registration points\r\n      on our player. For instance, if he is moving left, we test the points on his left side: */\r\n      if (game.player.x - game.player.old_x < 0) {// test collision on left side of player if moving left\r\n\r\n        /* There are much more efficient ways to write this code without repeating\r\n        variable names and this big monsterous block of if statements, but I thought it\r\n        would be good to have specific variable names and less abstraction so you can\r\n        get an idea of what's going on. */\r\n        var left_column    = Math.floor(game.player.left / game.world.tile_size);\r\n        var bottom_row     = Math.floor(game.player.bottom / game.world.tile_size);\r\n        var value_at_index = game.world.map[bottom_row * game.world.columns + left_column];\r\n\r\n        if (value_at_index != 0) {// Check the bottom left point\r\n\r\n          game.collision[value_at_index](game.player, bottom_row, left_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n        var top_row    = Math.floor(game.player.top / game.world.tile_size);\r\n        value_at_index = game.world.map[top_row * game.world.columns + left_column];\r\n\r\n        if (value_at_index != 0) {// Check the top left point\r\n\r\n          game.collision[value_at_index](game.player, top_row, left_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n      } else if (game.player.x - game.player.old_x > 0) {// Is the player moving right?\r\n\r\n        var right_column   = Math.floor(game.player.right / game.world.tile_size);\r\n        var bottom_row     = Math.floor(game.player.bottom / game.world.tile_size);\r\n        var value_at_index = game.world.map[bottom_row * game.world.columns + right_column];\r\n\r\n        if (value_at_index != 0) {// Check the bottom right point\r\n\r\n          game.collision[value_at_index](game.player, bottom_row, right_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n        var top_row    = Math.floor(game.player.top / game.world.tile_size);\r\n        value_at_index = game.world.map[top_row * game.world.columns + right_column];\r\n\r\n        if (value_at_index != 0) {// Check the top right point\r\n\r\n          game.collision[value_at_index](game.player, top_row, right_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      if (game.player.y - game.player.old_y < 0) {\r\n\r\n        var left_column    = Math.floor(game.player.left / game.world.tile_size);\r\n        var top_row        = Math.floor(game.player.top / game.world.tile_size);\r\n        var value_at_index = game.world.map[top_row * game.world.columns + left_column];\r\n\r\n        if (value_at_index != 0) {// Check the top left point\r\n\r\n          game.collision[value_at_index](game.player, top_row, left_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n        var right_column = Math.floor(game.player.right / game.world.tile_size);\r\n        value_at_index = game.world.map[top_row * game.world.columns + right_column];\r\n\r\n        if (value_at_index != 0) {// Check the top right point\r\n\r\n          game.collision[value_at_index](game.player, top_row, right_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n      } else if (game.player.y - game.player.old_y > 0) {\r\n\r\n        var left_column    = Math.floor(game.player.left / game.world.tile_size);\r\n        var bottom_row     = Math.floor(game.player.bottom / game.world.tile_size);\r\n        var value_at_index = game.world.map[bottom_row * game.world.columns + left_column];\r\n\r\n        if (value_at_index != 0) {// Check the bottom left point\r\n\r\n          game.collision[value_at_index](game.player, bottom_row, left_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n        var right_column = Math.floor(game.player.right / game.world.tile_size);\r\n        value_at_index = game.world.map[bottom_row * game.world.columns + right_column];\r\n\r\n        if (value_at_index != 0) {// Check the bottom right point\r\n\r\n          game.collision[value_at_index](game.player, bottom_row, right_column);\r\n          display.output.innerHTML = \"last tile collided with: \" + value_at_index;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      game.player.velocity_x *= 0.9;\r\n      game.player.velocity_y *= 0.9;\r\n\r\n      display.render();\r\n\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  display.buffer.canvas.height = 200;\r\n  display.buffer.canvas.width = 320;\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n\r\n  display.resize();\r\n\r\n  game.loop();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/touch-controller/touch-controller.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  justify-content:center;\r\n  min-height:100%;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#ffffff;\r\n  justify-self:center;\r\n\r\n}\r\n"
  },
  {
    "path": "content/touch-controller/touch-controller.html",
    "content": "<!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 = \"width=device-width\">\r\n\r\n    <link href = \"touch-controller.css\" rel = \"stylesheet\" type = \"text/css\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Touch Controller</h1>\r\n\r\n    <p style = \"font-size: 1.25em\">touches: 0<br>- -</p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script src = \"touch-controller.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/touch-controller/touch-controller.js",
    "content": "// 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 it's purpose here is to be a button:\r\n  Button = function(x, y, width, height, color) {\r\n\r\n    this.active = false;\r\n    this.color = color;\r\n    this.height = height;\r\n    this.width = width;\r\n    this.x = x;\r\n    this.y = y;\r\n\r\n  }\r\n\r\n  Button.prototype = {\r\n\r\n    // returns true if the specified point lies within the rectangle:\r\n    containsPoint:function(x, y) {\r\n\r\n      // if the point is outside of the rectangle return false:\r\n      if (x < this.x || x > this.x + this.width || y < this.y || y > this.y + this.width) {\r\n\r\n        return false;\r\n\r\n      }\r\n\r\n      return true;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // handles everything to do with user input:\r\n  controller = {\r\n\r\n    buttons:[\r\n\r\n      new Button(0, 160, 60, 60, \"#f09000\"),\r\n      new Button(190, 160, 60, 60, \"#0090f0\"),\r\n      new Button(260, 160, 60, 60, \"#0090f0\")\r\n\r\n    ],\r\n\r\n    testButtons:function(target_touches) {\r\n\r\n      var button, index0, index1, touch;\r\n\r\n      // loop through all buttons:\r\n      for (index0 = this.buttons.length - 1; index0 > -1; -- index0) {\r\n\r\n        button = this.buttons[index0];\r\n        button.active = false;\r\n\r\n        // loop through all touch objects:\r\n        for (index1 = target_touches.length - 1; index1 > -1; -- index1) {\r\n\r\n          touch = target_touches[index1];\r\n\r\n          // make sure the touch coordinates are adjusted for both the canvas offset and the scale ratio of the buffer and output canvases:\r\n          if (button.containsPoint((touch.clientX - display.bounding_rectangle.left) * display.buffer_output_ratio, (touch.clientY - display.bounding_rectangle.top) * display.buffer_output_ratio)) {\r\n\r\n            button.active = true;\r\n            break;// once the button is active, there's no need to check if any other points are inside, so continue\r\n\r\n          }\r\n\r\n        }\r\n\r\n      }\r\n\r\n      // this is all just for displaying the messages when buttons are pressed. This isn't necessary code.\r\n      display.message.innerHTML = \"touches: \" + event.targetTouches.length + \"<br>- \";\r\n\r\n      if (this.buttons[0].active) {\r\n\r\n        display.message.innerHTML += \"jump \";\r\n\r\n      }\r\n\r\n      if (this.buttons[1].active) {\r\n\r\n        display.message.innerHTML += \"left \";\r\n\r\n      }\r\n\r\n      if (this.buttons[2].active) {\r\n\r\n        display.message.innerHTML += \"right \";\r\n\r\n      }\r\n\r\n      display.message.innerHTML += \"-\";\r\n\r\n    },\r\n\r\n    touchEnd:function(event) {\r\n\r\n      event.preventDefault();\r\n      controller.testButtons(event.targetTouches);\r\n\r\n    },\r\n\r\n    touchMove:function(event) {\r\n\r\n      event.preventDefault();\r\n      controller.testButtons(event.targetTouches);\r\n\r\n    },\r\n\r\n    touchStart:function(event) {\r\n\r\n      event.preventDefault();\r\n      controller.testButtons(event.targetTouches);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // handles everything to do with displaying graphics on the screen:\r\n  display = {\r\n\r\n    // the buffer is used to scale the applications graphics to fit the screen:\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"),\r\n    // the on screen canvas context that we will be drawing to:\r\n    output:document.querySelector(\"canvas\").getContext(\"2d\"),\r\n    // the p element for text output:\r\n    message:document.querySelector(\"p\"),\r\n\r\n    // the ratio in size between the buffer and output canvases used to scale user input coordinates:\r\n    buffer_output_ratio:1,\r\n    // the bounding rectangle of the output canvas used to determine the location of user input on the output canvas:\r\n    bounding_rectangle:undefined,\r\n\r\n    // clears the display canvas to the specified color:\r\n    clear:function(color) {\r\n\r\n      this.buffer.fillStyle = color || \"#000000\";\r\n      this.buffer.fillRect(0, 0, this.buffer.canvas.width, this.buffer.canvas.height);\r\n\r\n    },\r\n\r\n    // renders the buffer to the output canvas:\r\n    render:function() {\r\n\r\n      this.output.drawImage(this.buffer.canvas, 0, 0, this.buffer.canvas.width, this.buffer.canvas.height, 0, 0, this.output.canvas.width, this.output.canvas.height);\r\n\r\n    },\r\n\r\n    // renders the buttons:\r\n    renderButtons:function(buttons) {\r\n\r\n      var button, index;\r\n\r\n      this.buffer.fillStyle = \"#202830\";\r\n      this.buffer.fillRect(0, 150, this.buffer.canvas.width, this.buffer.canvas.height);\r\n\r\n      for (index = buttons.length - 1; index > -1; -- index) {\r\n\r\n        button = buttons[index];\r\n\r\n        this.buffer.fillStyle = button.color;\r\n        this.buffer.fillRect(button.x, button.y, button.width, button.height);\r\n\r\n      }\r\n\r\n    },\r\n\r\n    // renders a square:\r\n    renderSquare:function(square) {\r\n\r\n      this.buffer.fillStyle = square.color;\r\n      this.buffer.fillRect(square.x, square.y, square.width, square.height);\r\n\r\n    },\r\n\r\n    // just keeps the output canvas element sized appropriately:\r\n    resize:function(event) {\r\n\r\n      display.output.canvas.width = Math.floor(document.documentElement.clientWidth - 32);\r\n\r\n      if (display.output.canvas.width > document.documentElement.clientHeight) {\r\n\r\n        display.output.canvas.width = Math.floor(document.documentElement.clientHeight);\r\n\r\n      }\r\n\r\n      display.output.canvas.height = Math.floor(display.output.canvas.width * 0.6875);\r\n\r\n      // these next two lines are used for adjusting and scaling user touch input coordinates:\r\n      display.bounding_rectangle = display.output.canvas.getBoundingClientRect();\r\n\r\n      display.buffer_output_ratio = display.buffer.canvas.width / display.output.canvas.width;\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // handles game logic:\r\n  game = {\r\n\r\n    loop:function(time_stamp) {\r\n\r\n      if (controller.buttons[0].active && game.square.jumping == false) {\r\n\r\n        game.square.velocity_y = -20;\r\n        game.square.jumping = true;\r\n\r\n      }\r\n\r\n      if (controller.buttons[1].active) {\r\n\r\n        game.square.velocity_x -= 0.5;\r\n\r\n      }\r\n\r\n      if (controller.buttons[2].active) {\r\n\r\n        game.square.velocity_x += 0.5;\r\n\r\n      }\r\n\r\n\r\n      // simulate gravity:\r\n      game.square.velocity_y += 1.5;\r\n\r\n      // simulate friction:\r\n      game.square.velocity_x *= 0.9;\r\n      game.square.velocity_y *= 0.9;\r\n\r\n      // move the square:\r\n      game.square.x += game.square.velocity_x;\r\n      game.square.y += game.square.velocity_y;\r\n\r\n      // collision detection for the square and the boundaries of the graphics buffer:\r\n      if (game.square.x + game.square.width < 0) {\r\n\r\n        game.square.x = display.buffer.canvas.width;\r\n\r\n      } else if (game.square.x > display.buffer.canvas.width) {\r\n\r\n        game.square.x = 0;\r\n\r\n      }\r\n\r\n      if (game.square.y + game.square.height > 150) {\r\n\r\n        game.square.y = 150 - game.square.height;\r\n        game.square.jumping = false;\r\n\r\n      }\r\n\r\n\r\n      display.clear(\"#303840\");\r\n\r\n      display.renderSquare(game.square);\r\n\r\n      display.renderButtons(controller.buttons);\r\n\r\n      display.render();\r\n\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    },\r\n\r\n    square: {\r\n\r\n      color:\"#ff0000\",\r\n      height:32,\r\n      jumping:true,\r\n      velocity_x:0,\r\n      velocity_y:0,\r\n      width:32,\r\n      x:0,\r\n      y:0,\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // initialize the application\r\n\r\n  // size the buffer:\r\n  display.buffer.canvas.height = 220;\r\n  display.buffer.canvas.width = 320;\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  // setting passive:false allows you to use preventDefault in event listeners:\r\n  display.output.canvas.addEventListener(\"touchend\", controller.touchEnd, {passive:false});\r\n  display.output.canvas.addEventListener(\"touchmove\", controller.touchMove, {passive:false});\r\n  display.output.canvas.addEventListener(\"touchstart\", controller.touchStart, {passive:false});\r\n\r\n  // make sure the display canvas is the appropriate size on the screen:\r\n  display.resize();\r\n\r\n  // start the game loop:\r\n  game.loop();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/vector-math/vector-math.html",
    "content": "<!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 vector math with full JavaScript source code.\">\r\n    <meta name = \"viewport\" content = \"width=device-width\">\r\n\r\n    <style>\r\n      * { box-sizing:border-box; margin:0; overflow:hidden; padding:0; pointer-events:none; }\r\n      body, html { height:100%; width:100%; }\r\n      body { display:grid; }\r\n      canvas { align-self:center; justify-self:center; }\r\n      div { left:0; padding:8px; position:absolute; top:0; width:100%; }\r\n      p { word-break:break-all; color:#ffffff; text-align:justify; user-select:none; width:100%; }\r\n      a { color:#f08000; cursor:pointer; padding:4px; pointer-events:all; }\r\n    </style>\r\n\r\n    <title>Vector Math</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <div>\r\n\r\n      <a data-function = \"angle\">Angle</a>\r\n      <a data-function = \"crossProduct\">Cross Product</a>\r\n      <a data-function = \"dotProduct\">Dot Product</a>\r\n      <a data-function = \"length\">Length</a>\r\n      <br><br>\r\n      <p>Choose an example to see how it works</p>\r\n\r\n    </div>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      class Shape2D {\r\n\r\n        constructor(x, y) { this.x = x; this.y = y; }\r\n\r\n        moveTo(x, y) {}\r\n\r\n      }\r\n\r\n      class Circle2D extends Shape2D {\r\n\r\n        constructor(x, y, r) {\r\n\r\n          super();\r\n\r\n          this.x = x; this.y = y; this.r = r;\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          this.x += x - this.x;\r\n          this.y += y - this.y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class LineSegment2D extends Shape2D {\r\n\r\n        constructor(x, y, x0, y0, x1, y1) {\r\n\r\n          super();\r\n\r\n          this.x = x; this.y = y;\r\n\r\n          this.point0 = new Point2D(x + x0, y + y0);\r\n          this.point1 = new Point2D(x + x1, y + y1);\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          var vector_x = x - this.x;\r\n          var vector_y = y - this.y;\r\n\r\n          this.point0.x += vector_x;\r\n          this.point0.y += vector_y;\r\n          this.point1.x += vector_x;\r\n          this.point1.y += vector_y;\r\n\r\n          this.x += vector_x;\r\n          this.y += vector_y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      class Point2D { constructor(x, y) { this.x = x; this.y = y; } }\r\n\r\n      class Polygon2D extends Shape2D {\r\n\r\n        constructor(x, y, d, ...vertices) {\r\n\r\n          super();\r\n\r\n          this.x = x; this.y = y; this.d = d; // direction in radians.\r\n          this.vertices = new Array();\r\n\r\n          for (let index = vertices.length - 2; index > -1; index -= 2) {\r\n\r\n            this.vertices[index * 0.5] = new Point2D(vertices[index] + x, vertices[index + 1] + y);\r\n\r\n          }\r\n\r\n        }\r\n\r\n        moveTo(x, y) {\r\n\r\n          var vector_x = x - this.x;\r\n          var vector_y = y - this.y;\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n\r\n            vertex.x += vector_x;\r\n            vertex.y += vector_y;\r\n\r\n          }\r\n\r\n          this.x += vector_x;\r\n          this.y += vector_y;\r\n\r\n        }\r\n\r\n        rotateTo(d) {\r\n\r\n          var direction = d - this.d;\r\n          this.d += direction;\r\n\r\n          var vector_x = Math.cos(direction);\r\n          var vector_y = Math.sin(direction);\r\n\r\n          for (let index = this.vertices.length - 1; index > -1; -- index) {\r\n\r\n            let vertex = this.vertices[index];\r\n\r\n            let x = vertex.x - this.x;\r\n            let y = vertex.y - this.y;\r\n\r\n            vertex.x = x * vector_x - y * vector_y + this.x;\r\n            vertex.y = x * vector_y + y * vector_x + this.y;\r\n\r\n          }\r\n          \r\n        }\r\n\r\n      }\r\n\r\n      var animation_frame_request = undefined;\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\", { alpha:false });\r\n      var output  = document.querySelector(\"p\");\r\n\r\n      var circle0   = new Circle2D(0, 0, 6);\r\n      var segment0  = new LineSegment2D(0, 0, 0, 0, 40, 0);\r\n      var triangle0 = new Polygon2D(0, 0, 0, -10, -10, 10, 0, -10, 10);\r\n      var vector0   = new Point2D(undefined, undefined);\r\n\r\n      var circle1   = new Circle2D(0, 0, 6);\r\n      var segment1  = new LineSegment2D(0, 0, 0, 0, 0, 40);\r\n      var triangle1 = new Polygon2D(0, 0, 0, -10, -10, 10, 0, -10, 10);\r\n      var vector1   = new Point2D(undefined, undefined);\r\n\r\n      var pointer = { down:false, x:undefined, y:undefined };\r\n\r\n      var selected_shape = undefined;\r\n      \r\n      var vector_math = (vector0, vector1) => {\r\n\r\n        output.innerHTML = \"Choose an example to see how it works!\";\r\n\r\n      };\r\n\r\n        ///////////////////////////////\r\n       //// Vector Math Functions ////\r\n      ///////////////////////////////\r\n\r\n      function angle(vector0, vector1) {\r\n\r\n        var dot_product    = vector0.x * vector1.x + vector0.y * vector1.y;\r\n        var cross_product  = vector0.x * vector1.y + vector0.y * vector1.x;\r\n        \r\n        var vector0_length = Math.sqrt(vector0.x * vector0.x + vector0.y * vector0.y);\r\n        var vector1_length = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y);\r\n        \r\n        var angle          = Math.acos(dot_product / (vector0_length * vector1_length)) * 180 / Math.PI;\r\n\r\n        angle              = Math.atan2(Math.abs(cross_product), dot_product) * 180 / Math.PI;\r\n\r\n        if (cross_product > 0) angle = 360 - angle;\r\n\r\n        output.innerHTML = \"The angle is: \" + angle + \"<br><br>Angle = acos(dot_product / (v0.length * v1.length)) * 180 / PI<br><br>or<br><br>Angle = atan2(abs(cross_product), dot_product) * 180 / PI<br><br>optional:<br><br>if (cross_product < 0) Angle = 360 - Angle\";\r\n\r\n      }\r\n\r\n      function crossProduct(vector0, vector1) {\r\n\r\n        var cross_product = vector0.x * vector1.y - vector0.y * vector1.x;\r\n\r\n        output.innerHTML = \"The cross product is: \" + cross_product + \"<br><br>Cross Product = v0.x * v1.y - v0.y * v1.x\";\r\n\r\n      }\r\n\r\n      function dotProduct(vector0, vector1) {\r\n\r\n        var dot_product = vector0.x * vector1.x + vector0.y * vector1.y;\r\n\r\n        output.innerHTML = \"The dot product is: \" + dot_product + \"<br><br>Dot Product = v0.x * v1.x + v0.y * v1.y\";\r\n\r\n      }\r\n\r\n      function length(vector0, vector1) {\r\n\r\n        var vector0_length = Math.sqrt(vector0.x * vector0.x + vector0.y * vector0.y);\r\n        var vector1_length = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y);\r\n\r\n        output.innerHTML = \"The length of vector0 is: \" + vector0_length + \"<br>The length of vector1 is: \" + vector1_length + \"<br><br>Length = sqrt(v.x * v.x + v.y * v.y)\";\r\n\r\n      }\r\n\r\n        ///////////////////////////\r\n       //// Drawing Functions ////\r\n      ///////////////////////////\r\n\r\n      function drawCircle(circle, color, line_w = 2) {\r\n\r\n        context.beginPath();\r\n        context.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2);\r\n        context.closePath();\r\n        if (line_w == 0) {\r\n          context.fillStyle = color;\r\n          context.fill();\r\n        } else {\r\n          context.strokeStyle = color;\r\n          context.lineWidth   = line_w;\r\n          context.stroke();\r\n        }\r\n\r\n      }\r\n\r\n      function drawPolygon(polygon, color, line_w = 2) {\r\n\r\n        context.beginPath();\r\n\r\n        var point = polygon.vertices[0];\r\n        context.moveTo(point.x, point.y);\r\n\r\n        for (let index = polygon.vertices.length - 1; index > 0; -- index) {\r\n\r\n          point = polygon.vertices[index];\r\n          context.lineTo(point.x, point.y);\r\n\r\n        }\r\n\r\n        context.closePath();\r\n\r\n        if (line_w == 0) {\r\n          context.fillStyle = color;\r\n          context.fill();\r\n        } else {\r\n          context.strokeStyle = color;\r\n          context.lineWidth   = line_w;\r\n          context.stroke();\r\n        }\r\n\r\n      }\r\n\r\n      function drawSegment(segment, color, line_w = 2) { \r\n\r\n        context.beginPath();\r\n        context.moveTo(segment.point0.x, segment.point0.y);\r\n        context.lineTo(segment.point1.x, segment.point1.y);\r\n        context.strokeStyle = color;\r\n        context.lineWidth   = line_w;\r\n        context.stroke();\r\n\r\n      }\r\n\r\n        ///////////////\r\n       //// Logic ////\r\n      ///////////////\r\n\r\n      function select(shape, r) {\r\n\r\n        var vector_x = pointer.x - shape.x;\r\n        var vector_y = pointer.y - shape.y;\r\n\r\n        if (vector_x * vector_x + vector_y * vector_y < r * r) {\r\n\r\n          selected_shape = shape;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function snap(circle0, circle1, r) {\r\n\r\n        var vector_x = circle1.x - circle0.x;\r\n        var vector_y = circle1.y - circle0.y;\r\n\r\n        if (vector_x * vector_x + vector_y * vector_y < r * r) {\r\n\r\n          circle0.x = circle1.x;\r\n          circle0.y = circle1.y;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      function loop() {\r\n\r\n        animation_frame_request = window.requestAnimationFrame(loop);\r\n\r\n        if (pointer.down && selected_shape == undefined) {\r\n      \r\n          select(circle0, 20);\r\n          select(circle1, 20);\r\n          select(triangle0, 20);\r\n          select(triangle1, 20);\r\n\r\n        } else if (!pointer.down) selected_shape = undefined;\r\n\r\n        if (selected_shape != undefined) selected_shape.moveTo(Math.round(pointer.x), Math.round(pointer.y));\r\n\r\n        if (selected_shape == circle0) snap(circle0, circle1, 20);\r\n        if (selected_shape == circle1) snap(circle1, circle0, 20);\r\n\r\n        segment0.point0.x = circle0.x;\r\n        segment0.point0.y = circle0.y;\r\n        segment0.point1.x = triangle0.x;\r\n        segment0.point1.y = triangle0.y;\r\n\r\n        segment1.point0.x = circle1.x;\r\n        segment1.point0.y = circle1.y;\r\n        segment1.point1.x = triangle1.x;\r\n        segment1.point1.y = triangle1.y;\r\n\r\n        vector0.x = segment0.point1.x - segment0.point0.x;\r\n        vector0.y = segment0.point1.y - segment0.point0.y;\r\n        vector1.x = segment1.point1.x - segment1.point0.x;\r\n        vector1.y = segment1.point1.y - segment1.point0.y;\r\n\r\n        triangle0.rotateTo(Math.atan2(vector0.y, vector0.x));\r\n        triangle1.rotateTo(Math.atan2(vector1.y, vector1.x));\r\n\r\n        vector_math(vector0, vector1);\r\n\r\n        context.fillStyle = \"#202020\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n        var gap      = 48;\r\n        var columns  = Math.ceil(context.canvas.width / gap);\r\n        var rows     = Math.ceil(context.canvas.height / gap);\r\n        var offset_x = Math.round((context.canvas.width - columns * gap) * 0.5);\r\n        var offset_y = Math.round((context.canvas.height - rows * gap) * 0.5);\r\n\r\n        for (let index = columns * rows; index > -1; -- index) {\r\n\r\n          let row = Math.floor(index / columns);\r\n          let column = index % columns;\r\n          let x = column * gap + offset_x;\r\n          let y = row * gap + offset_y;\r\n          \r\n          context.fillStyle = ((row & 1) == (column & 1)) ? \"#000000\" : \"#101018\";\r\n          context.fillRect(x, y, gap, gap);\r\n\r\n        }\r\n\r\n        drawSegment(this.segment0,  \"#0080f0\", 4);\r\n        drawCircle(this.circle0,    \"#0080f0\", 0);\r\n        drawPolygon(this.triangle0, \"#f08000\", 0);\r\n        drawSegment(this.segment1,  \"#0080f0\", 4);\r\n        drawCircle(this.circle1,    \"#0080f0\", 0);\r\n        drawPolygon(this.triangle1, \"#0080f0\", 0);\r\n\r\n        context.fillStyle = \"#ffffff\";\r\n        context.font = \"12px Arial\";\r\n        context.fillText(\"v0 ( \" + vector0.x + \", \" + vector0.y + \" )\", triangle0.x + vector0.x, triangle0.y + vector0.y);\r\n        context.fillText(\"v1 ( \" + vector1.x + \", \" + vector1.y + \" )\", triangle1.x + vector1.x, triangle1.y + vector1.y);\r\n\r\n      }\r\n\r\n        /////////////////////////\r\n       //// Event Listeners ////\r\n      /////////////////////////\r\n\r\n      function clickTouchStart(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        vector_math = window[this.dataset.function];\r\n\r\n      }\r\n\r\n      function mouseDownMoveUp(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n        pointer.x = event.clientX - rectangle.left;\r\n        pointer.y = event.clientY - rectangle.top;\r\n\r\n        if (event.type == \"mousedown\") pointer.down    = true;\r\n        else if (event.type == \"mouseup\") pointer.down = false;\r\n\r\n      }\r\n\r\n      function resize(event) {\r\n\r\n        context.canvas.height = document.documentElement.clientHeight;\r\n        context.canvas.width = document.documentElement.clientWidth;\r\n        context.imageSmoothingEnabled = false;\r\n\r\n        segment0.moveTo (Math.round(context.canvas.width * 0.5), Math.round(context.canvas.height * 0.5));\r\n        circle0.moveTo  (segment0.point0.x,                      segment0.point0.y);\r\n        triangle0.moveTo(segment0.point1.x,                      segment0.point1.y);\r\n        segment1.moveTo (segment0.x,                             segment0.y);\r\n        circle1.moveTo  (segment1.point0.x,                      segment1.point0.y);\r\n        triangle1.moveTo(segment1.point1.x,                      segment1.point1.y);\r\n\r\n      }\r\n\r\n      function touchEndMoveStart(event) {\r\n\r\n        event.preventDefault();\r\n\r\n        var rectangle = context.canvas.getBoundingClientRect();\r\n\r\n        if (event.type != \"touchend\") {\r\n          pointer.down = true;\r\n          pointer.x = event.touches[0].clientX - rectangle.left;\r\n          pointer.y = event.touches[0].clientY - rectangle.top;\r\n        } else pointer.down = false;\r\n\r\n      }\r\n\r\n        ////////////////////\r\n       //// INITIALIZE ////\r\n      ////////////////////\r\n\r\n      var links = document.querySelectorAll(\"a\");\r\n\r\n      for (let index = links.length - 1; index > -1; -- index) {\r\n\r\n        let link = links[index];\r\n\r\n        link.addEventListener(\"click\", clickTouchStart);\r\n        link.addEventListener(\"touchstart\", clickTouchStart)\r\n\r\n      }\r\n\r\n      window.addEventListener(\"resize\",     resize);\r\n      window.addEventListener(\"mousedown\",  mouseDownMoveUp, { passive:false });\r\n      window.addEventListener(\"mousemove\",  mouseDownMoveUp, { passive:false });\r\n      window.addEventListener(\"mouseup\",    mouseDownMoveUp, { passive:false });\r\n      window.addEventListener(\"touchend\",   touchEndMoveStart, { passive:false });\r\n      window.addEventListener(\"touchmove\",  touchEndMoveStart, { passive:false });\r\n      window.addEventListener(\"touchstart\", touchEndMoveStart, { passive:false });\r\n\r\n      resize();\r\n      loop();\r\n    \r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>"
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  grid-template-columns:auto;\r\n  grid-template-rows:auto auto auto auto;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\ncanvas {\r\n\r\n  background-color:#303840;\r\n  justify-self:center;\r\n\r\n}\r\n"
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.html",
    "content": "<!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    <meta name = \"veiwport\" content = \"width=device-width\">\r\n    <meta name = \"description\" content = \"Learn how to calculate which tile your player character is standing on in a 2d tile based game world!\">\r\n\r\n    <title>PoP Vlog - Walk On Tiles</title>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>PoP Vlog - Walk On Tiles</h1>\r\n\r\n    <p>&nbsp;</p> <!-- the output p element -->\r\n\r\n    <canvas></canvas>\r\n\r\n    <p>Blue squares are invading! Quickly! Use the keyboard to wield the mighty yellow circle and defeat them!</p>\r\n\r\n    <script src = \"walk-on-tiles.js\" type = \"text/javascript\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/walk-on-tiles/walk-on-tiles.js",
    "content": "// Frank Poth 11/15/2017\r\n\r\n(function() { \"use strict\" // used to throw a compiler error if a variable is not propperly defined\r\n\r\n  // the three main parts of the program:\r\n  var controller, display, game;\r\n\r\n  // holds our controller specific code:\r\n  controller = {\r\n\r\n    down:false,\r\n    left:false,\r\n    right:false,\r\n    up:false,\r\n\r\n    keyUpDown:function(event) {\r\n\r\n      var key_state = (event.type == \"keydown\")?true:false;\r\n\r\n      switch(event.keyCode) {\r\n\r\n        case 37: controller.left = key_state; break; // left key\r\n        case 38: controller.up = key_state; break; // up key\r\n        case 39: controller.right = key_state; break; // right key\r\n        case 40: controller.down = key_state; break; // down key\r\n\r\n      }\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // holds display specific code used for interacting with HTML and drawing to\r\n  // the canvas:\r\n  display = {\r\n\r\n    buffer:document.createElement(\"canvas\").getContext(\"2d\"), // used to draw image in game dimensions\r\n    context:document.querySelector(\"canvas\").getContext(\"2d\"), // used to display final scaled image in browser window\r\n    output:document.querySelector(\"p\"), // used to show output in browser window\r\n\r\n    render:function() {\r\n\r\n      // draw the tile map\r\n      for (let index = game.world.map.length - 1; index > -1; -- index) {\r\n\r\n        this.buffer.fillStyle = (game.world.map[index] == 1)?\"#0099ff\":\"#303840\"; // I have another tutorial that explains the math behind converting 1d map coordinates to 2d world coordinates\r\n        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);\r\n\r\n      }\r\n\r\n      // draw the player\r\n      this.buffer.fillStyle = game.player.color;\r\n      this.buffer.beginPath();\r\n      this.buffer.arc(game.player.x, game.player.y, game.player.radius, 0, Math.PI * 2);\r\n      this.buffer.closePath();\r\n      this.buffer.fill();\r\n\r\n      // draw the buffer to the display context. this will automatically scale your image.\r\n      // note that drawing this huge image on every frame is pretty memory intensive, and is a bad technique\r\n      // for an actual game. I just use it in my examples because it is quick to set up and takes care of scaling.\r\n      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);\r\n\r\n      // output all that nice data!\r\n      this.output.innerHTML = \"tile_x: \" + game.player.tile_x + \"<br>tile_y: \" + game.player.tile_y + \"<br>map index: \" + game.player.tile_y + \" * \" + game.world.columns + \" + \" + game.player.tile_x + \" = \" + String(game.player.tile_y * game.world.columns + game.player.tile_x);\r\n\r\n    },\r\n\r\n    // keep the canvas sized properly:\r\n    resize:function(event) {\r\n\r\n      var client_height = document.documentElement.clientHeight;\r\n\r\n      display.context.canvas.width = document.documentElement.clientWidth - 32;\r\n\r\n      if (display.context.canvas.width > client_height) {\r\n\r\n        display.context.canvas.width = client_height;\r\n\r\n      }\r\n\r\n      // everyone loves a 16/9 aspect ratio!\r\n      display.context.canvas.height = Math.floor(display.context.canvas.width * 0.5625);\r\n\r\n      display.render();// render the display again so you don't see a flicker on screen resize\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // holds game logic code:\r\n  game = {\r\n\r\n    // when the counter reaches 0, a tile turns blue\r\n    counter:Math.random() * 100,\r\n\r\n    // the player is just a yellow circle, doing yellow circle things:\r\n    player: {\r\n\r\n      color:\"#ff9900\",\r\n      radius:8,\r\n      tile_x:undefined,// the x and y tile positions of the player\r\n      tile_y:undefined,\r\n      velocity_x:0,\r\n      velocity_y:0,\r\n      x:160,// center screen\r\n      y:90\r\n\r\n    },\r\n\r\n    // the world object holds information about the game world, such as the\r\n    // level map and its dimensions as well as tile size\r\n    world: {\r\n\r\n      columns:16,// there are 16 columns and 9 rows in the map\r\n      rows:9,\r\n      tile_size:20,// each tile is 20 pixels wide and high\r\n\r\n      // the one dimensional tile map:\r\n      map:[1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n           1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n           1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n           1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\r\n           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,\r\n           1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0],\r\n\r\n    },\r\n\r\n    // this is the game loop. it is perpetuated by requestAnimationFrame\r\n    loop:function() {\r\n\r\n      // get input from the controller\r\n      if (controller.down) {\r\n\r\n        game.player.velocity_y += 0.25;\r\n\r\n      }\r\n\r\n      if (controller.left) {\r\n\r\n        game.player.velocity_x -= 0.25;\r\n\r\n      }\r\n\r\n      if (controller.right) {\r\n\r\n        game.player.velocity_x += 0.25;\r\n\r\n      }\r\n\r\n      if (controller.up) {\r\n\r\n        game.player.velocity_y -= 0.25;\r\n\r\n      }\r\n\r\n      // do some collision detection with the edge of the screen\r\n\r\n      // if the player is off of the left side of the screen\r\n      if (game.player.x - game.player.radius < 0) {\r\n\r\n        game.player.velocity_x = 0;// stop it from moving\r\n        game.player.x = game.player.radius;// reposition it\r\n\r\n      } else if (game.player.x + game.player.radius > display.buffer.canvas.width) { // or the right side of the screen\r\n\r\n        game.player.velocity_x = 0;\r\n        game.player.x = display.buffer.canvas.width - game.player.radius;\r\n\r\n      }\r\n\r\n      if (game.player.y - game.player.radius < 0) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.y = game.player.radius;\r\n\r\n      } else if (game.player.y + game.player.radius > display.buffer.canvas.height) {\r\n\r\n        game.player.velocity_y = 0;\r\n        game.player.y = display.buffer.canvas.height - game.player.radius;\r\n\r\n      }\r\n\r\n      // add the player's velocity to its x and y positions\r\n      game.player.x += game.player.velocity_x;\r\n      game.player.y += game.player.velocity_y;\r\n\r\n      // simulate friction and slow the player down\r\n      game.player.velocity_x *= 0.9;\r\n      game.player.velocity_y *= 0.9;\r\n\r\n      // here is all the important stuff:\r\n      // calculate the x and y tile in the map that the player is standing on\r\n      game.player.tile_x = Math.floor(game.player.x / game.world.tile_size);\r\n      game.player.tile_y = Math.floor(game.player.y / game.world.tile_size);\r\n\r\n      // set the tile the player is standing on to 0 in the map\r\n      game.world.map[game.player.tile_y * game.world.columns + game.player.tile_x] = 0;\r\n\r\n      // now we will test for win conditions\r\n      let victory = true;\r\n\r\n      // we must check every tile to see if there are any 1s left (1s are blue tiles)\r\n      for (let index = game.world.map.length - 1; index > -1; -- index) {\r\n\r\n        // if any 1s exist, we cannot win and there's no point in continuing the for loop\r\n        if (game.world.map[index] == 1) {\r\n\r\n          victory = false;\r\n          break;\r\n\r\n        }\r\n\r\n      }\r\n\r\n      // that's right, you can win this \"game\"\r\n      if (victory) {\r\n\r\n        // reset the controller so the alert prompt doesn't freeze the keydown event and make the player keep moving in its current direction\r\n        controller.down = controller.left = controller.right = controller.up = false;\r\n        game.counter = -1;// reset the counter to -1 so another blue square can be generated immediately\r\n        alert(\"You have done it! You have vanquished the evil blue squares! But they will rise again...\");\r\n\r\n      }\r\n\r\n      // when the counter reaches zero, make a random tile in the map a 1\r\n      game.counter --;\r\n\r\n      if (game.counter < 0) {\r\n\r\n        game.counter = Math.random() * 125;// reset the counter to a random value between 0 and 125\r\n        game.world.map[Math.floor(Math.random() * game.world.map.length)] = 1;// reset that random tile!\r\n\r\n      }\r\n\r\n      // draw all the tiles, the player, and output data\r\n      display.render();\r\n\r\n      // call the loop function again once the window is ready to draw\r\n      window.requestAnimationFrame(game.loop);\r\n\r\n    }\r\n\r\n  };\r\n\r\n  // initialize the application by setting default values:\r\n\r\n  // set the buffer size. this will be the world dimensions.\r\n  display.buffer.canvas.height = 180;\r\n  display.buffer.canvas.width = 320;\r\n\r\n  window.addEventListener(\"resize\", display.resize);\r\n  window.addEventListener(\"keydown\", controller.keyUpDown);\r\n  window.addEventListener(\"keyup\", controller.keyUpDown);\r\n\r\n  // size the canvas element\r\n  display.resize();\r\n\r\n  // start the game loop!\r\n  game.loop();\r\n\r\n})();\r\n"
  },
  {
    "path": "content/web-app/manifest.json",
    "content": "{\r\n\r\n  \"author\": \"PoP Vlog\",\r\n  \"background_color\": \"#ffffff\",\r\n  \"description\": \"Progressive Web App Example\",\r\n  \"display\": \"fullscreen\",\r\n  \"icons\": [\r\n\r\n    {\r\n      \"src\": \"https://192.168.0.101:2468/web-app.png\",\r\n      \"sizes\": \"192x192\",\r\n      \"type\": \"image/png\"\r\n    }\r\n\r\n  ],\r\n  \"manifest_version\": 2,\r\n  \"name\": \"Web App\",\r\n  \"orientation\": \"portrait\",\r\n  \"short_name\": \"Web App\",\r\n  \"start_url\": \"https://192.168.0.101:2468/web-app.html\",\r\n  \"theme_color\": \"#ffffff\",\r\n  \"version\": \"0.1\"\r\n\r\n}\r\n"
  },
  {
    "path": "content/web-app/server.js",
    "content": "// 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\"); // file system\r\n  https = require(\"https\"); // creates an https server\r\n  path = require(\"path\"); // used for working with url paths\r\n\r\n  // used to create response headers\r\n  /* If the user requests a .css file, we want to ensure we attach \"text/css\" to\r\n  our response header, this way the browser knows how to handle it. */\r\n  mimetypes = {\r\n\r\n    \"css\":\"text/css\",\r\n    \"html\":\"text/html\",\r\n    \"ico\":\"image/ico\",\r\n    \"jpg\":\"image/jpeg\",\r\n    \"js\":\"text/javascript\",\r\n    \"json\":\"application/json\",\r\n    \"png\":\"image/png\"\r\n\r\n  };\r\n\r\n  options = {\r\n\r\n    pfx: fs.readFileSync(\"ssl/crt.pfx\"),\r\n    passphrase: \"password\"\r\n\r\n  };\r\n\r\n  // Start a secure server that uses the credentials in ssl/crt.pfx\r\n  server = https.createServer(options, function(request, response) {\r\n\r\n    /* When requesting the homepage of a website, we usually only type\r\n    www.mysite.com, but the server returns www.mysite.com/index.html. To make\r\n    it easier for users to access our site, we add \"/index.html\" to their url\r\n    so the user doesn't have to type out the whole address of our home page. */\r\n\r\n    // If the url is empty\r\n    if (request.url == \"\" || request.url == \"/\") {\r\n\r\n      // The user is requesting the home page of the website, so give it to them\r\n      request.url = \"web-app.html\";\r\n\r\n    }\r\n\r\n    // Next we read the file at the requested url and write it to the document.\r\n    /* __dirname is just the base directory of your website, so if your website\r\n    is www.coolsite.com, then __dirname is www.coolsite.com. When you put it all\r\n    together it looks like www.coolsite.com/index.html or whatever the requested\r\n    url is */\r\n    fs.readFile(__dirname + \"/\" + request.url, function(error, content) {\r\n\r\n      if (error) { // if there is an error reading the requested url\r\n\r\n        console.log(\"Error: \" + error); // output it to the console\r\n\r\n      } else { // else, there is no error, write the file contents to the page\r\n\r\n        // 200 is code for OK, and the second parameter is our content header\r\n        response.writeHead(200, {'Content-Type':mimetypes[path.extname(request.url).split(\".\")[1]]});\r\n        response.write(content); // write that content to our response object\r\n\r\n      }\r\n\r\n      response.end(); // This will send our response object to the browser\r\n\r\n    });\r\n\r\n  });\r\n\r\n  server.listen(\"2468\", \"192.168.0.101\", function() {\r\n\r\n    console.log(\"Server started!\");\r\n\r\n  }); // listen on 192.168.0.101:2468\r\n\r\n})();\r\n"
  },
  {
    "path": "content/web-app/web-app.css",
    "content": "/* 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%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  align-content:space-around;\r\n  background-color:#202830;\r\n  color:#ffffff;\r\n  display:grid;\r\n  height:100%;\r\n  justify-content:space-around;\r\n  justify-items:space-around;\r\n  padding:8px;\r\n  text-align:center;\r\n  width:100%;\r\n\r\n}\r\n\r\nimg {\r\n\r\n  margin:0 auto;\r\n\r\n}\r\n\r\np {\r\n\r\n  font-size:1.1em;\r\n  text-align:justify;\r\n\r\n}\r\n"
  },
  {
    "path": "content/web-app/web-app.html",
    "content": "<!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-scalable=no, width=device-width\">\r\n\r\n    <link href = \"manifest.json\" rel = \"manifest\">\r\n    <link href = \"web-app.css\" rel = \"stylesheet\" type = \"text/css\">\r\n    <link href = \"web-app.png\" rel = \"icon\" type = \"image/png\">\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <h1>Android Web App!</h1>\r\n\r\n    <img src = \"web-app.png\">\r\n\r\n    <p>This page can be viewed in any browser, but it can also work in a web app! If you are viewing this page in a full screened webview on your mobile device, you are looking at a fully functional web app! You can use this technology to better connect with your users or create a full screen mobile experience for your HTML5 games!</p>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/wmw-basic/basic.html",
    "content": "<!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-Basics</title>\r\n\r\n    <style>\r\n\r\n      * { box-sizing:border-box; margin:0; padding:0; }\r\n\r\n      html { height:100%; width:100%; }\r\n\r\n      body {\r\n\r\n        align-content:space-around;\r\n        background-color:#202830;\r\n        color:#ffffff;\r\n        display:grid;\r\n        justify-content:center;\r\n        font-size:1.25em;\r\n        min-height:100%;\r\n        width:100%;\r\n\r\n      }\r\n\r\n      p { text-align:center; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <p>Learn some basic html stuff.</p>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var render = function() {\r\n\r\n        context.canvas.width = document.documentElement.clientWidth * 0.5;\r\n        context.canvas.height = document.documentElement.clientHeight * 0.5;\r\n\r\n        context.fillStyle = \"#008000\";\r\n        context.fillRect(0, 0, context.canvas.width, context.canvas.height);\r\n\r\n      };\r\n\r\n      window.addEventListener(\"resize\", render);\r\n\r\n      render();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "content/wmw-bouncing-balls/bouncing-balls.html",
    "content": "<!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 Bounce</title>\r\n\r\n    <style>\r\n\r\n      * { margin:0; padding:0; }\r\n\r\n      canvas { height:100%; position:fixed; width:100%; }\r\n\r\n    </style>\r\n\r\n  </head>\r\n\r\n  <body>\r\n\r\n    <canvas></canvas>\r\n\r\n    <script type = \"text/javascript\">\r\n\r\n      const Ball = function(x, y, radius) {\r\n\r\n        this.color = \"rgb(\" + Math.floor(Math.random() * 256) + \",\" + Math.floor(Math.random() * 256) + \",\" + Math.floor(Math.random() * 256) + \")\";\r\n        this.direction = Math.random() * Math.PI * 2;\r\n        this.radius = radius;\r\n        this.speed = Math.random() * 3 + 1;\r\n        this.x = x;\r\n        this.y = y;\r\n\r\n      };\r\n\r\n      Ball.prototype = {\r\n\r\n        updatePosition:function(width, height) {\r\n\r\n          this.x += Math.cos(this.direction) * this.speed;\r\n          this.y += Math.sin(this.direction) * this.speed;\r\n\r\n          if(this.x - this.radius < 0) {\r\n\r\n            this.x = this.radius;\r\n\r\n            this.direction = Math.atan2(Math.sin(this.direction), Math.cos(this.direction) * -1);\r\n\r\n          } else if (this.x + this.radius > width) {\r\n\r\n            this.x = width - this.radius;\r\n\r\n            this.direction = Math.atan2(Math.sin(this.direction), Math.cos(this.direction) * -1);\r\n\r\n          }\r\n\r\n          if(this.y - this.radius < 0) {\r\n\r\n            this.y = this.radius;\r\n\r\n            this.direction = Math.atan2(Math.sin(this.direction) * -1, Math.cos(this.direction));\r\n\r\n          } else if (this.y + this.radius > height) {\r\n\r\n            this.y = height - this.radius;\r\n\r\n            this.direction = Math.atan2(Math.sin(this.direction) * -1, Math.cos(this.direction));\r\n\r\n          }\r\n\r\n        }\r\n\r\n      };\r\n\r\n      var context = document.querySelector(\"canvas\").getContext(\"2d\");\r\n\r\n      var balls = new Array();\r\n\r\n      let x = document.documentElement.clientWidth * 0.5;\r\n      let y = document.documentElement.clientHeight * 0.5;\r\n\r\n      for(let index = 0; index < 50; index ++) {\r\n\r\n        balls.push(new Ball(x, y, Math.floor(Math.random() * 10 + 20)));\r\n\r\n      }\r\n\r\n      function loop() {\r\n\r\n        window.requestAnimationFrame(loop);\r\n\r\n        let height = document.documentElement.clientHeight;\r\n        let width  = document.documentElement.clientWidth;\r\n\r\n        context.canvas.height = height;\r\n        context.canvas.width = width;\r\n\r\n        /* I removed that - 1 after making the video. It's not neccessary. */\r\n        for(let index = 0; index < balls.length; index ++) {\r\n\r\n          let ball = balls[index];\r\n\r\n          context.fillStyle = ball.color;\r\n          context.beginPath();\r\n          context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);\r\n          context.fill();\r\n\r\n          ball.updatePosition(width, height);\r\n\r\n        }\r\n\r\n      }\r\n\r\n      loop();\r\n\r\n    </script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "data/logs.json",
    "content": "[\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 writing code and twisting my brain in knots over interesting programming concepts. I recently posted part 17 of the Stay Down Dev Log and I intend to finish that project and post the end result to itch.io. Feel free to reach out to me in the comments on YouTube. As always, I hope you learn something while you're here! Have a good one!\",\r\n    \"date\":\"2021-09-06\"\r\n  },\r\n  {\r\n    \"name\":\"Rumble.com\",\r\n    \"note\":\"I recently discovered Rumble.com which I'm going to use in addition to YouTube. If anyone wants to check out my channel there, click the link at the top right of this page! Stay down is still the latest project and is receiving regular updates, so check back to see the game's progress!\",\r\n    \"date\":\"2020-12-30\"\r\n  },\r\n  {\r\n    \"name\":\"Stay Down Devlog:\",\r\n    \"note\":\"Lately I've been working on a small game I plan to publish on itch.io. The idea is that platforms push you up and if you don't stay down you die. Pretty genious, I know, but the real goal of the project is to share with everyone how it's made. If you follow the videos you'll learn about fixed time step loops, game states, collision detection, and much more. Hopefully by the time I'm finished you'll have enough knowledge to publish your own fully featured game on itch.io.\",\r\n    \"date\":\"2020-09-05\"\r\n  },\r\n  {\r\n    \"name\":\"Project Trajectory\",\r\n    \"note\":\"My latest projects focus on improving rendering performance by reducing draw calls. These improvements will greatly improve the speed of your HTML5 game.\",\r\n    \"date\":\"2020-06-14\"\r\n  },\r\n  {\r\n    \"name\":\"New Website!\",\r\n    \"note\":\"This is the new website. You can search for projects and easily access related videos and source code. Here in the logs section I'll post about the latest project or whatever other updates I think might be helpful.\",\r\n    \"date\":\"2019-12-24\"\r\n  }\r\n]"
  },
  {
    "path": "data/projects.json",
    "content": "[\r\n  {\r\n    \"name\":\"Stay Down\",\r\n    \"note\":\"Try to stay down while collecting the items. Keyboard controls.\",\r\n    \"path\":\"stay-down\",\r\n    \"page\":\"stay-down\",\r\n    \"vlog\":\"-8SmBDbwAsw&list=PLcN6MkgfgN4D5Fo9zL9xlaSxL-KD7iN84\",\r\n    \"date\":\"2020-08-01\",\r\n    \"tags\":\"js,platformer\"\r\n  },\r\n  {\r\n    \"name\":\"Better Tile with Graphics\",\r\n    \"note\":\"Uses a canvas buffer to improve rendering performance. Shows how to load a tile sheet image and draw a tile map.\",\r\n    \"path\":\"better-tile-graphics\",\r\n    \"page\":\"better-tile-graphics\",\r\n    \"vlog\":\"\",\r\n    \"date\":\"2020-06-14\",\r\n    \"tags\":\"js,tile,performance,graphics,fetch\"\r\n  },\r\n  {\r\n    \"name\":\"Better Tile\",\r\n    \"note\":\"A more efficient way to draw a tile map.\",\r\n    \"path\":\"better-tile\",\r\n    \"page\":\"better-tile\",\r\n    \"vlog\":\"XmmvblvSOho\",\r\n    \"date\":\"2019-12-31\",\r\n    \"tags\":\"js,tile,performance\"\r\n  },\r\n  {\r\n    \"name\":\"Bouncing Polygons\",\r\n    \"note\":\"Bounce convex polygons off of a non-axis aligned line.\",\r\n    \"path\":\"bouncing-polygons\",\r\n    \"page\":\"bouncing-polygons\",\r\n    \"vlog\":\"Newm6jnp7T0\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Polygon Basics\",\r\n    \"note\":\"A good place to get started with 2D polygons.\",\r\n    \"path\":\"polygon\",\r\n    \"page\":\"polygon\",\r\n    \"vlog\":\"3kICmEJ-xyo\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"GJK Collision\",\r\n    \"note\":\"There is no video for this project (yet).\",\r\n    \"path\":\"gjk\",\r\n    \"page\":\"gjk\",\r\n    \"vlog\":\"\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Polygon Rotation\",\r\n    \"note\":\"There is no video for this project.\",\r\n    \"path\":\"polygon-rotation\",\r\n    \"page\":\"polygon-rotation\",\r\n    \"vlog\":\"\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Hitbox\",\r\n    \"note\":\"Showcases moving hitboxes, tunneling, and non-tile based platforming.\",\r\n    \"path\":\"hitbox\",\r\n    \"page\":\"hitbox\",\r\n    \"vlog\":\"VpSWuywFlC8\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,collision,tunneling\"\r\n  },\r\n  {\r\n    \"name\":\"Vector Math\",\r\n    \"note\":\"There are multiple videos that go with this example. How do you get to them? I haven't implemented that functionality yet.\",\r\n    \"path\":\"vector-math\",\r\n    \"page\":\"vector-math\",\r\n    \"vlog\":\"b5TjpTBW6yw\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Particle Pool\",\r\n    \"note\":\"Use object pooling to conserve memory when using lots of objects.\",\r\n    \"path\":\"particle-pool\",\r\n    \"page\":\"particle-pool\",\r\n    \"vlog\":\"9dp0mAc2vvY\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Simple Inventory\",\r\n    \"note\":\"A very basic inventory system where you click an item to take it out of the world and put it into an array.\",\r\n    \"path\":\"inventory\",\r\n    \"page\":\"inventory\",\r\n    \"vlog\":\"AKJMf73H1Jc\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Tile Animation\",\r\n    \"note\":\"Animate some background tiles based on frame rate.\",\r\n    \"path\":\"tile-animation\",\r\n    \"page\":\"tile-animation\",\r\n    \"vlog\":\"AQABpi9nLfU\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Rectangle Collision\",\r\n    \"note\":\"Collision between two rectangles.\",\r\n    \"path\":\"rectangle-collision\",\r\n    \"page\":\"rectangle-collision\",\r\n    \"vlog\":\"LYrge3ylccQ\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Moving Platforms\",\r\n    \"note\":\"Moving platforms for a 2D platforming game.\",\r\n    \"path\":\"platform\",\r\n    \"page\":\"platform\",\r\n    \"vlog\":\"1eHAFNUPlk8\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,platformer\"\r\n  },\r\n  {\r\n    \"name\":\"Platformer AI\",\r\n    \"note\":\"Shows how to make a few different AI types for a tile based platformer.\",\r\n    \"path\":\"platformer-ai\",\r\n    \"page\":\"platformer-ai\",\r\n    \"vlog\":\"zbqwFb8DJgQ\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,platformer\"\r\n  },\r\n  {\r\n    \"name\":\"Shoot\",\r\n    \"note\":\"Very basic shooting mechanism for 2D platformers. Click to shoot.\",\r\n    \"path\":\"shoot\",\r\n    \"page\":\"shoot\",\r\n    \"vlog\":\"WML5a8QRtnw\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,platformer\"\r\n  },\r\n  {\r\n    \"name\":\"Scrolling Tile Background\",\r\n    \"note\":\"Showcases a scrolling tile background as well as some very simple terrain interaction.\",\r\n    \"path\":\"tile-scroll\",\r\n    \"page\":\"tile-scroll\",\r\n    \"vlog\":\"jabYMh9sI8Q\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,scrolling,side-scroller\"\r\n  },\r\n  {\r\n    \"name\":\"3D Cube\",\r\n    \"note\":\"A 3D cube rendered on the CPU using nothing but pure JavaScript\",\r\n    \"path\":\"cube\",\r\n    \"page\":\"cube\",\r\n    \"vlog\":\"OVQxTNd2U3w\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,3d\"\r\n  },\r\n  {\r\n    \"name\":\"Pseudo 3D Starfield\",\r\n    \"note\":\"A 2D starfield with a 3D effect. Uses a valid 3D projection technique.\",\r\n    \"path\":\"starfield\",\r\n    \"page\":\"starfield\",\r\n    \"vlog\":\"jtWvkNK2LVo\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,3d\"\r\n  },\r\n  {\r\n    \"name\":\"Bouncing Balls\",\r\n    \"note\":\"Bouncing balls that use directional/polar movement rather than axis aligned movement.\",\r\n    \"path\":\"wmw-bouncing-balls\",\r\n    \"page\":\"bouncing-balls\",\r\n    \"vlog\":\"hoWjnidQOms\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js\"\r\n  },\r\n  {\r\n    \"name\":\"Basic HTML Layout\",\r\n    \"note\":\"My basic layout for projects. Provides a good starting point for small projects.\",\r\n    \"path\":\"wmw-basic\",\r\n    \"page\":\"basic\",\r\n    \"vlog\":\"hm7py_lZkL8\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"html,js\"\r\n  },\r\n  {\r\n    \"name\":\"Circle Collision Response\",\r\n    \"note\":\"Collision response technique for colliding circles.\",\r\n    \"path\":\"circle-collision-response\",\r\n    \"page\":\"circle-collision-response\",\r\n    \"vlog\":\"nlwtgvZCz0k\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,collision\"\r\n  },\r\n  {\r\n    \"name\":\"Circle Collision Detection\",\r\n    \"note\":\"Detect collision between two circles.\",\r\n    \"path\":\"circle-collision-detection\",\r\n    \"page\":\"circle-collision-detection\",\r\n    \"vlog\":\"STcP9P122_8\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,collision\"\r\n  },\r\n  {\r\n    \"name\":\"Input Processing Output (IPO)\",\r\n    \"note\":\"I'm probably going to remove this one. It kind of makes sense, but I'm not sure how relevant or helpful it is.\",\r\n    \"path\":\"ipo\",\r\n    \"page\":\"ipo\",\r\n    \"vlog\":\"PFd8Xw5C6Zs\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Rabbit Trap\",\r\n    \"note\":\"There are a few videos on this project. It's basically a how to build a platformer series that probably has too much overhead for what it is. I'm planning on redoing this one.\",\r\n    \"path\":\"rabbit-trap\",\r\n    \"page\":\"rabbit-trap\",\r\n    \"vlog\":\"opiWzi0KWjs\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS,platformer\"\r\n  },\r\n  {\r\n    \"name\":\"Pre-Scale Performance\",\r\n    \"note\":\"Tests out the performance of different scaling techniques. As of 2019, I prefer using CSS to scale.\",\r\n    \"path\":\"pre-scale-performance\",\r\n    \"page\":\"pre-scale-performance\",\r\n    \"vlog\":\"hQD4Yw3IyZ0\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Tile Graphics\",\r\n    \"note\":\"Draw graphics to your tile map.\",\r\n    \"path\":\"tile-graphics\",\r\n    \"page\":\"tile-graphics\",\r\n    \"vlog\":\"XELyA6ECDLk\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,tile,graphics\"\r\n  },\r\n  {\r\n    \"name\":\"Pagination\",\r\n    \"note\":\"Paginates some static content.\",\r\n    \"path\":\"pagination\",\r\n    \"page\":\"pagination\",\r\n    \"vlog\":\"7TSVnm5Fpj0\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Dominique's Doors\",\r\n    \"note\":\"A very basic level loading example using JSON.\",\r\n    \"path\":\"dominiques-doors\",\r\n    \"page\":\"dominiques-doors\",\r\n    \"vlog\":\"96Q200cPFss\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,json\"\r\n  },\r\n  {\r\n    \"name\":\"JSON\",\r\n    \"note\":\"A basic JSON example.\",\r\n    \"path\":\"json\",\r\n    \"page\":\"json\",\r\n    \"vlog\":\"jRuz3bXBDKc\",\r\n    \"date\":\"2018-mm-dd\",\r\n    \"tags\":\"js,json\"\r\n  },\r\n  {\r\n    \"name\":\"Calculator\",\r\n    \"note\":\"Make a JavaScript calculator. Note: It uses eval to calculate equations.\",\r\n    \"path\":\"calculator\",\r\n    \"page\":\"calculator\",\r\n    \"vlog\":\"ySB3YlKqQLA\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Blit\",\r\n    \"note\":\"Blitting graphics.\",\r\n    \"path\":\"blit\",\r\n    \"page\":\"blit\",\r\n    \"vlog\":\"sKf2vJBiBj0\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Dino Run\",\r\n    \"note\":\"A simple endless runner.\",\r\n    \"path\":\"dino\",\r\n    \"page\":\"dino\",\r\n    \"vlog\":\"LZ0w3HAQKWU\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS,game\"\r\n  },\r\n  {\r\n    \"name\":\"Sprite Animation\",\r\n    \"note\":\"Animate moving objects to make your game more interesting.\",\r\n    \"path\":\"animation\",\r\n    \"page\":\"animation\",\r\n    \"vlog\":\"5GxoVaO58NM\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,animation\"\r\n  },\r\n  {\r\n    \"name\":\"Load Image\",\r\n    \"note\":\"Load an image.\",\r\n    \"path\":\"load-image\",\r\n    \"page\":\"load-image\",\r\n    \"vlog\":\"cbBXjqaCzbs\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,loading\"\r\n  },\r\n  {\r\n    \"name\":\"Tile Types\",\r\n    \"note\":\"There are multiple videos for this example. This example covers the various types of tiles one might use in a 2D platformer.\",\r\n    \"path\":\"tile-types\",\r\n    \"page\":\"tile-types\",\r\n    \"vlog\":\"zRO6Q8VcHNE\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,tile,slope\"\r\n  },\r\n  {\r\n    \"name\":\"Snake\",\r\n    \"note\":\"The classic snake game.\",\r\n    \"path\":\"snake\",\r\n    \"page\":\"snake\",\r\n    \"vlog\":\"pxBW6FZglrI\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,game\"\r\n  },\r\n  {\r\n    \"name\":\"Top Down Tiles\",\r\n    \"note\":\"Top down tile based collision detection and response.\",\r\n    \"path\":\"top-down-tiles\",\r\n    \"page\":\"top-down-tiles\",\r\n    \"vlog\":\"nPVWL6RlJPE\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,tile,collision\"\r\n  },\r\n  {\r\n    \"name\":\"Hit The Wall\",\r\n    \"note\":\"2D platformer tile based collision detection and response.\",\r\n    \"path\":\"hit-the-wall\",\r\n    \"page\":\"hit-the-wall\",\r\n    \"vlog\":\"r-Y-N4cLd10\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Walk On Tiles\",\r\n    \"note\":\"Interact with the tile grid using the player and your keyboard.\",\r\n    \"path\":\"walk-on-tiles\",\r\n    \"page\":\"walk-on-tiles\",\r\n    \"vlog\":\"GN2Eh7UauwU\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Indexed DB\",\r\n    \"note\":\"An example that shows how to use Indexed DB.\",\r\n    \"path\":\"indexed-db\",\r\n    \"page\":\"index\",\r\n    \"vlog\":\"3y_x3Nkc8Dw\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Offline Web App\",\r\n    \"note\":\"Store the files your app needs in the browser so you can run your app offline.\",\r\n    \"path\":\"offline-web-app\",\r\n    \"page\":\"web-app\",\r\n    \"vlog\":\"11kCO6TxD0E\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Web App\",\r\n    \"note\":\"Create a web app that can run in full screen mode on any device.\",\r\n    \"path\":\"web-app\",\r\n    \"page\":\"web-app\",\r\n    \"vlog\":\"-xFC_oqF_Bw\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"HTTPS Server\",\r\n    \"note\":\"Make an HTTPS server in Node JS.\",\r\n    \"path\":\"https-server\",\r\n    \"page\":\"index\",\r\n    \"vlog\":\"xvMGNGaapG0\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Touch Controller\",\r\n    \"note\":\"Make a responsive touch controller for your mobile game.\",\r\n    \"path\":\"touch-controller\",\r\n    \"page\":\"touch-controller\",\r\n    \"vlog\":\"3M_aNkaFkiw\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Tile Grid\",\r\n    \"note\":\"Learn about tile map indexing and how to find your place in the map.\",\r\n    \"path\":\"tile-grid\",\r\n    \"page\":\"tile-grid\",\r\n    \"vlog\":\"c32wtgMeT0U\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Tile World\",\r\n    \"note\":\"Tile World\",\r\n    \"path\":\"tile-world\",\r\n    \"page\":\"tile-world\",\r\n    \"vlog\":\"WMTv6QsAp0I\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"js,tile\"\r\n  },\r\n  {\r\n    \"name\":\"Square Collision Response\",\r\n    \"note\":\"Square Collision\",\r\n    \"path\":\"square-collision-response\",\r\n    \"page\":\"response\",\r\n    \"vlog\":\"\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS,collision\"\r\n  },\r\n  {\r\n    \"name\":\"Collision\",\r\n    \"note\":\"Collision\",\r\n    \"path\":\"collision\",\r\n    \"page\":\"collision\",\r\n    \"vlog\":\"\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS,collision\"\r\n  },\r\n  {\r\n    \"name\":\"Control\",\r\n    \"note\":\"Control\",\r\n    \"path\":\"control\",\r\n    \"page\":\"control\",\r\n    \"vlog\":\"8uIt9a2XBrw\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS,controller\"\r\n  },\r\n  {\r\n    \"name\":\"Animation Game Loop\",\r\n    \"note\":\"Animation\",\r\n    \"path\":\"animation-game-loop\",\r\n    \"page\":\"animation\",\r\n    \"vlog\":\"fiVcbX-X_tc\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Canvas\",\r\n    \"note\":\"How to use HTML5 canvas\",\r\n    \"path\":\"canvas\",\r\n    \"page\":\"canvas\",\r\n    \"vlog\":\"N3MQCi-R6KY\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"HTML\"\r\n  },\r\n  {\r\n    \"name\":\"Elements\",\r\n    \"note\":\"Working with HTML elements.\",\r\n    \"path\":\"elements\",\r\n    \"page\":\"elements\",\r\n    \"vlog\":\"hNYE7ReLibg\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"HTML,JS\"\r\n  },\r\n  {\r\n    \"name\":\"Multiple-Inheritance\",\r\n    \"note\":\"Shows one way to approach multiple inheritance. I later discovered that this approach copies all parent properties, which is different from true inheritance. True inheritance uses the same properties rather than cloning them. I have still found this to be a useful technique that achieves a similar effect.\",\r\n    \"path\":\"multiple-inheritance\",\r\n    \"page\":\"multiple-inheritance\",\r\n    \"vlog\":\"y2XSsC1UsAY\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"inheritance,JS\"\r\n  },\r\n  {\r\n    \"name\":\"Prototype Inheritance\",\r\n    \"note\":\"Shows prototype inheritance in JS\",\r\n    \"path\":\"prototype-inheritance\",\r\n    \"page\":\"prototype-inheritance\",\r\n    \"vlog\":\"Z3y3Y1fHcaE\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"inheritance,JS\"\r\n  },\r\n  {\r\n    \"name\":\"Inheritance\",\r\n    \"note\":\"Shows basic class inheritance\",\r\n    \"path\":\"inheritance\",\r\n    \"page\":\"inheritance\",\r\n    \"vlog\":\"de4Qc49lCUU\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"inheritance,JS\"\r\n  },\r\n  {\r\n    \"name\":\"Objects And Vars\",\r\n    \"note\":\"Shows variable declaration in JavaScript\",\r\n    \"path\":\"objects-and-vars\",\r\n    \"page\":\"objects\",\r\n    \"vlog\":\"cPM-kl9WsCg\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  },\r\n  {\r\n    \"name\":\"Hello World\",\r\n    \"note\":\"A simple hello world example\",\r\n    \"path\":\"hello-world\",\r\n    \"page\":\"hello\",\r\n    \"vlog\":\"yv02WDcmxHk\",\r\n    \"date\":\"2017-mm-dd\",\r\n    \"tags\":\"JS\"\r\n  }\r\n]"
  },
  {
    "path": "index.css",
    "content": "/* 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 0px;\r\n\r\n}\r\n\r\n#main-filter-query {\r\n\r\n  align-content:center;\r\n  align-items:center;\r\n  border:2px solid var(--color-transparent);\r\n  border-radius:8px;\r\n  display:inline-flex;\r\n  margin:0px 8px 0px 0px;\r\n  min-width:50%;\r\n  padding:32px 8px;\r\n\r\n}\r\n\r\n#main-filter-query:empty {\r\n\r\n  caret-color:var(--color-transparent);\r\n\r\n}\r\n\r\n#main-filter-query:empty:before {\r\n\r\n  color:var(--color-medium-gray);\r\n  content:attr(data-placeholder);\r\n\r\n}\r\n\r\n#main-filter-query:focus {\r\n\r\n  border-color:var(--color-signature-yellow);\r\n\r\n}"
  },
  {
    "path": "index.html",
    "content": "<!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 = \"application/octet-stream\">\r\n    <link href = \"theme.css\"         rel = \"stylesheet\" type = \"text/css\">\r\n    <link href = \"index.css\"         rel = \"stylesheet\" type = \"text/css\">\r\n    <link href = \"log.css\"           rel = \"stylesheet\" type = \"text/css\">\r\n    <link href = \"project.css\"       rel = \"stylesheet\" type = \"text/css\">\r\n    \r\n    <meta charset = \"utf-8\">\r\n    <meta name = \"author\"       content = \"Frank Poth\">\r\n    <meta name = \"color-scheme\" content = \"only light\">\r\n    <meta name = \"description\"  content = \"Find video tutorials on game programming as well as working examples and source code!\">\r\n    <meta name = \"keywords\"     content = \"programming tutorials,game design tutorials,javascript tutorials,learn to make games,make a game from scratch,learn to code,programming for games\">\r\n    <meta name = \"robots\"       content = \"follow,index\">\r\n    <meta name = \"theme-color\"  content = \"#202830\">\r\n    <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\">\r\n\r\n    <script src = \"library/dom-kit.js\"></script>\r\n    <script src = \"tools/project.js\"></script>\r\n    <script src = \"tools/log.js\"></script>\r\n    \r\n    <title>Poth On Programming</title>\r\n\r\n  </head>\r\n  \r\n  <body>\r\n\r\n    <header>\r\n\r\n      <div id = \"header-logo\">\r\n        \r\n        <img class = \"logo-image\" src = \"media/brackets-64x64.png\">\r\n        <span id = \"header-logo-text\">Poth On Programming</span>\r\n    \r\n      </div>\r\n\r\n      <nav id = \"header-links\">\r\n\r\n        <a class = \"header-link\" href = \"https://github.com/pothonprogramming/pothonprogramming.github.io\">Github</a>\r\n        <a class = \"header-link\" href = \"https://www.youtube.com/channel/UCdS3ojA8RL8t1r18Gj1cl6w\">YouTube</a>\r\n        <a class = 'header-link' href = 'https://rumble.com/c/c-448127' style='text-decoration:underline'>Rumble</a>\r\n        <a class = \"header-link\" href = \"https://discord.gg/fTNxCXv\">Join-Discord</a>\r\n        <a class = 'header-link' href = '#donate' style='color:var(--color-signature-yellow)' target='_self'>Donate</a>\r\n\r\n      </nav>\r\n\r\n    </header>\r\n\r\n    <main>\r\n\r\n      <span class = \"banner\">Logs</span>\r\n\r\n      <div class = \"list-container\" id = \"main-logs-container\"></div>\r\n\r\n      <span class = \"banner\">Latest Project</span>\r\n\r\n      <div class = \"list-container\" id = \"main-project-container\"></div>\r\n\r\n      <span class = \"banner\">All Projects</span>\r\n        \r\n      <div id = \"main-filter-bar\">\r\n          \r\n        <div contenteditable = true data-placeholder = \"Enter query\" id = \"main-filter-query\"></div>\r\n        <div class = \"main-button\" id = \"main-filter-button\">Filter</div>\r\n        \r\n      </div>\r\n\r\n      <div class = \"list-container\" id = \"main-projects-container\"></div>\r\n\r\n    </main>\r\n\r\n    <footer>\r\n\r\n      <p id='donate'>If you'd like to help the project, consider donating! In return I'll keep posting source code and videos!</p>\r\n      \r\n      <p><a class = 'donate-button' href = 'https://paypal.me/pothonprogramming?locale.x=en_US'>Donate with PayPal!</a><br><a class = 'donate-button' href=\"https://www.patreon.com/bePatron?u=15382655\" data-patreon-widget-type=\"become-patron-button\">Become a Patron!</a></p>\r\n\r\n      <p>The goal of Poth On Programming is to produce high quality programming tutorials that are easy to access and learn from.</p>\r\n      \r\n      <img style='image-rendering:pixelated; image-rendering:crisp-edges; width:256px; height:256px;' class = \"logo-image\" src = \"media/brackets-64x64.png\">\r\n\r\n    </footer>\r\n\r\n    <script src = \"index.js\"></script>\r\n\r\n  </body>\r\n\r\n</html>\r\n"
  },
  {
    "path": "index.js",
    "content": "(() => {\r\n\r\n  const filter_query       = document.getElementById(\"main-filter-query\");\r\n  const project_container  = document.getElementById(\"main-project-container\");\r\n  const projects_container = document.getElementById(\"main-projects-container\");\r\n  const logs_container     = document.getElementById(\"main-logs-container\");\r\n\r\n  const projects = [];\r\n  const logs     = [];\r\n\r\n  function clickOrTouchStart(event) {\r\n\r\n    if (event.target.id == \"main-filter-button\") {\r\n      \r\n      event.preventDefault();\r\n      \r\n      filter(filter_query.innerText);\r\n\r\n    }\r\n\r\n  }\r\n\r\n  function emptyContainer(container) {\r\n\r\n    while(container.firstChild) container.removeChild(container.firstChild);\r\n\r\n  }\r\n\r\n  function fillContainer(container, elements) {\r\n\r\n    while(elements.length != 0) container.appendChild(elements.shift());\r\n\r\n  }\r\n\r\n  function filter(query) {\r\n\r\n    query = query.replace(/[\\n\\r\\v]/g, ''); // filter newline and carriage return\r\n\r\n    var elements = [];\r\n\r\n    for (var index = 1; index < projects.length; index ++) {\r\n\r\n      var project = projects[index];\r\n\r\n      var regexp = new RegExp(query, 'i');\r\n\r\n      if (regexp.test(project.data.name) || regexp.test(project.data.tags) || regexp.test(project.data.note) || regexp.test(project.data.date)) elements.push(project.element);\r\n\r\n    }\r\n\r\n    emptyContainer(projects_container);\r\n    fillContainer(projects_container, elements);\r\n\r\n  }\r\n\r\n  function keyDown(event) {\r\n\r\n    if (event.keyCode == 13) {\r\n     \r\n      event.preventDefault();\r\n      filter(filter_query.innerText);\r\n\r\n    }\r\n\r\n  }\r\n\r\n  // Open all links in new tab\r\n  (() => {\r\n\r\n    const links = document.querySelectorAll('a');\r\n  \r\n    for (var index = links.length - 1; index > -1; -- index) {\r\n     \r\n      var link = links[index];\r\n      if (link.target === '') link.setAttribute('target', '_blank');\r\n\r\n    }\r\n  \r\n  })();\r\n  \r\n\r\n  fetch(\"data/logs.json\").then(response => {\r\n\r\n    return response.json();\r\n\r\n  }).then(data => {\r\n\r\n    for (var index = 0; index < data.length; index ++) logs[index] = new Log(data[index]);\r\n\r\n    logs_container.appendChild(logs[0].element);\r\n\r\n  });\r\n\r\n  fetch(\"data/projects.json\").then(response => {\r\n\r\n    return response.json();\r\n\r\n  }).then(data => {\r\n\r\n    for (var index = 0; index < data.length; index ++) projects[index] = new Project(data[index]);\r\n\r\n    project_container.appendChild(projects[0].element);\r\n\r\n    filter(\"\");\r\n\r\n  });\r\n\r\n  window.addEventListener(\"touchstart\", clickOrTouchStart);\r\n\r\n  window.addEventListener(\"click\", clickOrTouchStart);\r\n\r\n  window.addEventListener(\"keydown\", keyDown);\r\n\r\n})();"
  },
  {
    "path": "library/dom-kit.js",
    "content": "// Frank Poth 08/18/2017\r\n\r\nconst DOMKit = function() {};\r\n\r\nDOMKit.createElement = function(tag_name, attributes, content) {\r\n\r\n  var element = document.createElement(tag_name);\r\n\r\n  if (attributes) DOMKit.setAttributes(element, attributes);\r\n\r\n  if (content) element.innerHTML = content;\r\n\r\n  return element;\r\n\r\n};\r\n\r\nDOMKit.parseHTMLString = function(string) {\r\n\r\n  var fragment = document.createDocumentFragment();\r\n  fragment.innerHTML = string;\r\n\r\n  alert(fragment.children[0]);\r\n\r\n  return fragment.children[0];\r\n\r\n};\r\n\r\n// created 03/27/2018\r\nDOMKit.replaceCurrentScript = function(element) {\r\n\r\n  var script = document.currentScript;\r\n\r\n  script.parentNode.replaceChild(element, script);\r\n\r\n  return element;\r\n\r\n};\r\n\r\nDOMKit.replaceElement = function(element, new_element) {\r\n\r\n  element.parentNode.replaceChild(new_element, element);\r\n\r\n  return new_element;\r\n\r\n};\r\n\r\nDOMKit.setAttributes = function(element, attributes) {\r\n\r\n  var attribute_pair;\r\n\r\n  for (index = attributes.length - 1; index > -1; -- index) {\r\n\r\n    attribute_pair = attributes[index].split(/=(.+)?/, 2);\r\n\r\n    element.setAttribute(attribute_pair[0], attribute_pair[1]);\r\n\r\n  }\r\n\r\n};\r\n"
  },
  {
    "path": "log.css",
    "content": "/* 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  background-color:var(--color-light-gray);\r\n  display:inline-block;\r\n  padding:0px 4px;\r\n  text-align:right;\r\n  user-select:none;\r\n\r\n}\r\n\r\n.log-name {\r\n\r\n  display:inline;\r\n  font-weight:800;\r\n  font-size:1.25em;\r\n  text-align:left;\r\n  user-select:none;\r\n\r\n}\r\n\r\n.log-name:before, .log-name:after {\r\n\r\n  content:\" \";\r\n\r\n}\r\n\r\n.log-note {\r\n\r\n  display:inline;\r\n  padding:4px 0px;\r\n  text-align:justify;\r\n\r\n}"
  },
  {
    "path": "project.css",
    "content": "/* 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 {\r\n\r\n  background-color:var(--color-light-blue);\r\n  color:var(--color-signature-gray);\r\n  cursor:pointer;\r\n  display:inline-block;\r\n  margin:0px 0px 4px;\r\n  padding:4px 8px;\r\n\r\n}\r\n\r\n.project-link:hover {\r\n\r\n  background-color:var(--color-white);\r\n\r\n}\r\n\r\n.project-links {\r\n\r\n  text-align:left;\r\n\r\n}\r\n\r\n.project-name {\r\n\r\n  color:var(--color-signature-gray);\r\n  display:inline-block;\r\n  font-size:1.25em;\r\n  font-weight:800;\r\n  padding:4px 8px;\r\n  text-align:left;\r\n  user-select:none;\r\n  width:100%;\r\n\r\n}\r\n\r\n.project-note {\r\n\r\n  color:var(--color-signature-gray);\r\n  display:inline-block;\r\n  padding:0px 4px 4px 8px;\r\n  text-align:left;\r\n  user-select:none;\r\n  width:100%;\r\n\r\n}"
  },
  {
    "path": "robots.txt",
    "content": "User-agent: * \r\nDisallow: \r\nCrawl-delay: 15"
  },
  {
    "path": "server.js",
    "content": "// 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.MY_PORT || \"3000\";\r\n\r\n  var fs, https, mimetypes, options, path, server;\r\n\r\n  fs = require(\"fs\"); // file system\r\n  http = require(\"http\"); // creates an http server\r\n  path = require(\"path\"); // used for working with url paths\r\n\r\n  // used to create response headers\r\n  /* If the user requests a .css file, we want to ensure we attach \"text/css\" to\r\n  our response header, this way the browser knows how to handle it. */\r\n  mimetypes = {\r\n\r\n    \"css\":\"text/css\",\r\n    \"html\":\"text/html\",\r\n    \"ico\":\"image/ico\",\r\n    \"jpg\":\"image/jpeg\",\r\n    \"js\":\"text/javascript\",\r\n    \"json\":\"application/json\",\r\n    \"png\":\"image/png\",\r\n    \"txt\":\"text/plain\"\r\n\r\n  };\r\n\r\n  server = http.createServer((request, response) => {\r\n\r\n    console.log(request.url);\r\n\r\n    if (request.url == \"\" || request.url == \"/\") {\r\n\r\n      // The user is requesting the home page of the website, so give it to them\r\n      request.url = \"index.html\";\r\n\r\n    }\r\n\r\n    request.url = request.url.split(\"?\")[0];\r\n\r\n    fs.readFile(__dirname + \"/\" + request.url, function(error, content) {\r\n\r\n      if (error) { // if there is an error reading the requested url\r\n\r\n        console.log(\"Error: \" + error); // output it to the console\r\n\r\n      } else { // else, there is no error, write the file contents to the page\r\n\r\n        let extension = path.extname(request.url).split(\".\")[1];\r\n\r\n        if (!mimetypes[extension]) extension = \"txt\";\r\n\r\n        // 200 is code for OK, and the second parameter is our content header\r\n        response.writeHead(200, {'Content-Type':mimetypes[extension]});\r\n        response.write(content); // write that content to our response object\r\n\r\n      }\r\n\r\n      response.end(); // This will send our response object to the browser\r\n\r\n    });\r\n\r\n  });\r\n\r\n  server.listen(port, ip, function() {\r\n\r\n    console.log(\"Server started at \" + ip + \":\" + port + \"!\");\r\n\r\n  });\r\n\r\n})();\r\n"
  },
  {
    "path": "theme.css",
    "content": "/* 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-blue:#d0d8f0;\r\n  --color-light-gray:#e0e8f0;\r\n  --color-lighter-blue:#e0e8ff;\r\n  --color-lighter-gray:#f0f8ff;\r\n  --color-medium-gray:#606870;\r\n  --color-signature-gray:#202830;\r\n  --color-signature-yellow:#f0c000;\r\n  --color-transparent:rgba(0,0,0,0);\r\n  --color-white:#ffffff;\r\n  --header-height:64px;\r\n\r\n}\r\n\r\n* {\r\n  \r\n  box-sizing:border-box;\r\n  color:var(--color-signature-gray);\r\n  font-family:Consolas, monospace;\r\n  margin:0;\r\n  overflow:hidden;\r\n  padding:0;\r\n  text-decoration:none;\r\n\r\n}\r\n\r\n*:focus {\r\n\r\n  outline:none;\r\n\r\n}\r\n\r\nbody, html {\r\n\r\n  height:100%;\r\n  width:100%;\r\n\r\n}\r\n\r\nbody {\r\n\r\n  background-color:var(--color-white);\r\n  overflow:auto;\r\n\r\n}\r\n\r\nfooter {\r\n\r\n  align-content:space-around;\r\n  align-items:space-around;\r\n  background-color:var(--color-signature-gray);\r\n  border-top:solid 1px var(--color-light-gray);\r\n  color:#ffffff;\r\n  display:grid;\r\n  flex-direction:column;\r\n  height:auto;\r\n  justify-content:center;\r\n  justify-items:center;\r\n  min-height:100%;\r\n  overflow-wrap: break-word;\r\n  padding:32px 0;\r\n  width:100%;\r\n  word-break:break-word;\r\n  word-wrap: break-word;\r\n\r\n}\r\n\r\nfooter a {\r\n\r\n  color:var(--color-signature-yellow);\r\n\r\n}\r\n\r\nfooter p {\r\n\r\n  color:var(--color-light-gray);\r\n  max-width:800px;\r\n  padding:4px;\r\n\r\n}\r\n\r\nheader {\r\n\r\n  align-content:center;\r\n  align-items:center;\r\n  background-color:var(--color-signature-gray);\r\n  border-bottom:solid 1px var(--color-light-gray);\r\n  display:flex;\r\n  flex-wrap:wrap;\r\n  justify-content:space-around;\r\n  justify-items:space-between;\r\n  min-height:var(--header-height);\r\n  padding:8px 4px 8px 4px;\r\n\r\n}\r\n\r\nmain {\r\n\r\n  display:inline-block;\r\n  min-height:calc(100% - var(--header-height));\r\n  width:100%;\r\n\r\n}\r\n\r\n.banner {\r\n\r\n  background-color:var(--color-signature-gray);\r\n  color:var(--color-signature-yellow);\r\n  display:block;\r\n  font-size:1.5em;\r\n  padding:8px 0px 8px 8px;\r\n  text-align:left;\r\n  user-select:none;\r\n  width:100%;\r\n\r\n}\r\n\r\n.donate-button {\r\n\r\n  align-items:center;\r\n  background-color:var(--color-signature-yellow);\r\n  border:none;\r\n  border-radius:8px;\r\n  box-shadow:0px 4px var(--color-dark-yellow);\r\n  color:var(--color-signature-gray);\r\n  cursor:pointer;\r\n  display:grid;\r\n  justify-items:center;\r\n  margin:0 0 4px 0;\r\n  max-width:312px;\r\n  padding:8px 32px;\r\n  text-align:center;\r\n\r\n}\r\n\r\n.donate-button:focus, .donate-button:focus-visible {\r\n\r\n  outline-color:rgba(0, 0, 0, 0);\r\n  outline-style:none;\r\n  outline-width:0px;\r\n\r\n}\r\n\r\n.donate-button:hover {\r\n\r\n  background-color:var(--color-signature-yellow);\r\n  box-shadow:0px 0px rgba(0, 0, 0, 0.5);\r\n  margin:4px 0 0 0;\r\n  overflow:visible;\r\n  position:relative;\r\n\r\n}\r\n\r\n.donate-button:hover::before {\r\n\r\n  background-color:rgba(0, 0, 0, 0);\r\n  content:\"\";\r\n  height:100%;\r\n  left:0px;\r\n  position:absolute;\r\n  top:-4px;\r\n  width:100%;\r\n\r\n}\r\n\r\n.header-link {\r\n\r\n  color:var(--color-light-gray);\r\n  cursor:pointer;\r\n  padding:12px 8px 8px 8px;\r\n\r\n}\r\n\r\n.header-link:hover {\r\n\r\n  color:var(--color-white);\r\n\r\n}\r\n\r\n.list-container {\r\n\r\n  align-items:flex-start;\r\n  display:flex;\r\n  flex-direction:column;\r\n\r\n}\r\n\r\n.logo-image {\r\n\r\n  display:inline-block;\r\n  height:var(--header-height);\r\n  image-rendering:pixelated;\r\n  margin:0px 12px 0px 0px;\r\n  width:auto;\r\n\r\n}\r\n\r\n.main-button {\r\n\r\n  align-items:center;\r\n  background-color:var(--color-light-blue);\r\n  color:var(--color-signature-gray);\r\n  cursor:pointer;\r\n  display:inline-flex;\r\n  padding:8px;\r\n  user-select:none;\r\n\r\n}\r\n\r\n.main-button:hover {\r\n\r\n  background-color:var(--color-white);\r\n\r\n}\r\n\r\n#header-links {\r\n\r\n  display:flex;\r\n  flex-wrap:wrap;\r\n  justify-content:left;\r\n  justify-self:stretch;\r\n\r\n}\r\n\r\n#header-logo {\r\n\r\n  align-content:center;\r\n  align-items:center;\r\n  display:inline-flex;\r\n\r\n}\r\n\r\n#header-logo-text {\r\n\r\n  color:var(--color-signature-yellow);\r\n  display:inline;\r\n  font-size:2.0em;\r\n  font-weight:800;\r\n  text-align:left;\r\n  word-wrap: wrap break-word;\r\n\r\n}"
  },
  {
    "path": "tools/log.js",
    "content": "const Log = function(data) {\r\n\r\n  this.data    = data;\r\n  this.element = document.createRange().createContextualFragment('<div class = \"log\"><span class = \"log-date\">' + data.date + '</span><span class = \"log-name\">' + data.name + '</span><p class = \"log-note\">' + data.note + '</p></div>').children[0];\r\n\r\n};"
  },
  {
    "path": "tools/project.js",
    "content": "const Project = function(data) {\r\n\r\n  var vlog = data.vlog ? '<a class = \"project-link\" href = \"https://www.youtube.com/watch?v=' + data.vlog + '\" target = \"_blank\">video</a>' : '';\r\n\r\n  this.data    = data;\r\n  this.element = document.createRange().createContextualFragment('<div class = \"project\"><span class = \"project-name\">' + data.name + '</span><nav class = \"project-links\"><a class = \"project-link\" href = \"https://github.com/pothonprogramming/pothonprogramming.github.io/tree/master/content/' + data.path + '\" target = \"_blank\">code</a><a class = \"project-link\" href = \"content/' + data.path + '/' + data.page + '.html' + '\" target = \"_blank\">page</a>' + vlog + '</nav><p class = \"project-note\">' + data.note + '</p></div>').children[0];\r\n\r\n};"
  }
]