Commits

Cat's Eye Technologies  committed 9369bb8

Add some common files from yoob.js version 0.6.

  • Participants
  • Parent commits 4791daa

Comments (0)

Files changed (3)

File common-yoob.js-0.6/animation.js

+/*
+ * This file is part of yoob.js version 0.6
+ * Available from https://github.com/catseye/yoob.js/
+ * This file is in the public domain.  See http://unlicense.org/ for details.
+ */
+if (window.yoob === undefined) yoob = {};
+
+/*
+ * Pretty standard shim to get window.{request,cancelRequest}AnimationFrame
+ * functions, synthesized from the theory and the many examples I've seen.
+ */
+
+window.requestAnimationFrame =
+    window.requestAnimationFrame ||
+    window.webkitRequestAnimationFrame ||
+    window.mozRequestAnimationFrame ||
+    window.oRequestAnimationFrame ||
+    window.msRequestAnimationFrame ||
+    function(f, elem) {
+        return setTimeout(function() {
+            f(Date.now());
+        }, 1000 / 60);
+    };
+
+// it was called "cancelRequestAnimationFrame" in the editor's draft:
+// http://webstuff.nfshost.com/anim-timing/Overview.html
+// but "cancelAnimationFrame" in the Candidate Recommendation:
+// http://www.w3.org/TR/animation-timing/
+window.cancelAnimationFrame =
+    window.cancelAnimationFrame ||
+    window.webkitCancelAnimationFrame ||
+    window.mozCancelAnimationFrame ||
+    window.oCancelAnimationFrame ||
+    window.msCancelAnimationFrame ||
+    window.cancelRequestAnimationFrame ||
+    window.webkitCancelRequestAnimationFrame ||
+    window.mozCancelRequestAnimationFrame ||
+    window.oCancelRequestAnimationFrame ||
+    window.msCancelRequestAnimationFrame ||
+    clearTimeout;
+window.cancelRequestAnimationFrame = window.cancelAnimationFrame;
+
+/*
+ * A yoob.Animation object manages an animation.
+ *
+ * How many things get animated by one yoob.Animation object is up to
+ * you.  For animated demos, it may be sufficient to have only one
+ * Animation object which updates many independent graphical objects.
+ * However, it may be useful to have multiple Animation objects, if
+ * the program can be in different states (for example, one title screen
+ * Animation, and a different Animation to use during gameplay.)
+ */
+yoob.Animation = function() {
+    /*
+     * Initialize a yoob.Animation.  Takes a configuration dictionary:
+     *
+     *   mode          'quantum' (default) or 'proportional'
+     *   object        the object to call methods on
+     *   tickTime      in msec.  for quantum only. default = 1/60th sec
+     *   lastTime      internal (but see below)
+     *   accumDelta    internal, only used in quantum mode
+     *
+     * There are two modes that a yoob.Animation can be in,
+     * 'quantum' (the default) and 'proportional'.
+     *
+     * Once the Animation has been started (by calling animation.start()):
+     *
+     * In the 'quantum' mode, the object's draw() method is called on
+     * each animation frame, and the object's update() method is called as
+     * many times as necessary to ensure it is called once for every tickTime
+     * milliseconds that have passed.  Neither method is passed any
+     * parameters.
+     *
+     * In the 'proportional' mode, the object's draw() method is called on
+     * each animation frame, and the amount of time (in milliseconds) that has
+     * elapsed since the last time it was called (or 0 if it was never
+     * previously called) is passed as the first and only parameter.
+     */
+    this.init = function(cfg) {
+        this.object = cfg.object;
+        this.lastTime = cfg.lastTime || null;
+        this.accumDelta = cfg.accumDelta || 0;
+        this.tickTime = cfg.tickTime || (1000.0 / 60.0);
+        this.mode = cfg.mode || 'quantum';
+        this.request = null;
+        return this;
+    };
+
+    this.start = function() {
+        if (this.request) {
+            return false;
+        }
+        var $this = this;
+        if (this.mode === 'quantum') {
+            var animFrame = function(time) {
+                $this.object.draw();
+                if ($this.lastTime === null) {
+                    $this.lastTime = time;
+                }
+                $this.accumDelta += (time - $this.lastTime);
+                while ($this.accumDelta > $this.tickTime) {
+                    $this.accumDelta -= $this.tickTime;
+                    $this.object.update();
+                }
+                $this.lastTime = time;
+                if ($this.request) {
+                    $this.request = requestAnimationFrame(animFrame);
+                }
+            };
+        } else if (this.mode === 'proportional') {
+            var animFrame = function(time) {
+                var timeElapsed = (
+                    $this.lastTime == null ? 0 : time - $this.lastTime
+                );
+                $this.lastTime = time;
+                $this.object.draw(timeElapsed);
+                if ($this.request) {
+                    $this.request = requestAnimationFrame(animFrame);
+                }
+            };
+        }
+        this.request = requestAnimationFrame(animFrame);
+        return true;
+    };
+
+    this.stop = function() {
+        if (this.request) {
+            cancelRequestAnimationFrame(this.request);
+        }
+        this.request = null;
+    };
+
+    this.reset = function() {
+        this.stop();
+        this.lastTime = null;
+    };
+};

File common-yoob.js-0.6/preset-manager.js

+/*
+ * This file is part of yoob.js version 0.6
+ * Available from https://github.com/catseye/yoob.js/
+ * This file is in the public domain.  See http://unlicense.org/ for details.
+ */
+if (window.yoob === undefined) yoob = {};
+
+/*
+ * An object for managing a set of "presets" -- which, for an esolang,
+ * might be example programs; for an emulator, might be ROM images;
+ * for a control panel, may be pre-selected combinations of settings;
+ * and so forth.
+ *
+ * Mostly intended to be connected to a yoob.Controller -- but need not be.
+ */
+yoob.PresetManager = function() {
+    /*
+     * The single argument is a dictionary (object) where the keys are:
+     *
+     *    selectElem: (required) the <select> DOM element that will be
+     *        populated with the available example programs.  Selecting one
+     *        will cause the .select() method of this manager to be called.
+     *        it will also call .onselect if that method is present.
+     *
+     *    controller: a yoob.Controller (or compatible object) that will
+     *        be informed of the selection, if no callback was supplied
+     *        when the item was added.
+     */
+    this.init = function(cfg) {
+        this.selectElem = cfg.selectElem;
+        this.controller = cfg.controller || null;
+        this.clear();
+        var $this = this;
+        this.selectElem.onchange = function() {
+            $this._select(this.options[this.selectedIndex].value);
+        }
+        return this;
+    };
+
+    /*
+     * Removes all options from the selectElem, and their associated data.
+     */
+    this.clear = function() {
+        this.reactTo = {};
+        while (this.selectElem.firstChild) {
+            this.selectElem.removeChild(this.selectElem.firstChild);
+        }
+        this.add('(select one...)', function() {});
+        return this;
+    };
+
+    /*
+     * Adds a preset to this PresetManager.  When it is selected,
+     * the given callback will be called, being passed the id as the
+     * first argument.  If no callback is provided, a default callback,
+     * which loads the contents of the element with the specified id
+     * into the configured yoob.Controller, will be used.
+     */
+    this.add = function(id, callback) {
+        var opt = document.createElement("option");
+        opt.text = id;
+        opt.value = id;
+        this.selectElem.options.add(opt);
+        var $this = this;
+        this.reactTo[id] = callback || function(id) {
+            $this.controller.click_stop(); // in case it is currently running
+            $this.controller.loadSourceFromHTML(
+              document.getElementById(id).innerHTML
+            );
+        };
+        return this;
+    };
+
+    /*
+     * Called by the selectElem's onchange event.  For sanity, you should
+     * probably not call this yourself.
+     */
+    this._select = function(id) {
+        this.reactTo[id](id);
+        if (this.onselect) {
+            this.onselect(id);
+        }
+    };
+
+    /*
+     * Call this to programmatically select an item.  This will change
+     * the selected option in the selectElem and trigger the appropriate
+     * callback in this PresetManager.
+     */
+    this.select = function(id) {
+        var i = 0;
+        var opt = this.selectElem.options[i];
+        while (opt) {
+            if (opt.value === id) {
+                this.selectElem.selectedIndex = i;
+                this._select(id);
+                return this;
+            }
+            i++;
+            opt = this.selectElem.options[i];
+        }
+        // if not found, select the "(select one...)" option
+        this.selectElem.selectedIndex = 0;
+        return this;
+    };
+
+    /*
+     * When called, every DOM element in the document with the given
+     * class will be considered a preset, and the manager
+     * will be populated with these.  Generally the CSS for the class
+     * will have `display: none` and the elements will be <div>s.
+     *
+     * callback is as described for the .add() method.
+     */
+    this.populateFromClass = function(className, callback) {
+        var elements = document.getElementsByClassName(className);
+        for (var i = 0; i < elements.length; i++) {
+            var e = elements[i];
+            this.add(e.id, callback);
+        }
+        return this;
+    };
+};

File common-yoob.js-0.6/sprite-manager.js

+/*
+ * This file is part of yoob.js version 0.6
+ * Available from https://github.com/catseye/yoob.js/
+ * This file is in the public domain.  See http://unlicense.org/ for details.
+ */
+if (window.yoob === undefined) yoob = {};
+
+/*
+ * This is really just an interface; duck-typing-wise, you can use any
+ * Javascript object you want as a sprite, so long as it exposes these
+ * methods.
+ */
+yoob.Sprite = function() {
+  this.isDraggable = false;
+  this.isClickable = false;
+
+  this.init = function(x, y, w, h) {
+    this.x = x;
+    this.y = y;
+    this.w = w;
+    this.h = h;
+    this.dx = 0;
+    this.dy = 0;
+    this.selected = false;
+  };
+
+  this.getX = function() {
+    return this.x;
+  };
+
+  this.getY = function() {
+    return this.y;
+  };
+
+  this.getCenterX = function() {
+    return this.x + this.w / 2;
+  };
+
+  this.getCenterY = function() {
+    return this.y + this.h / 2;
+  };
+
+  this.getWidth = function() {
+    return this.w;
+  };
+
+  this.getHeight = function() {
+    return this.h;
+  };
+
+  this.setVelocity = function(dx, dy) {
+    this.dx = dx;
+    this.dy = dy;
+  };
+
+  this.setDestination = function(x, y, ticks) {
+    this.destX = x;
+    this.destY = y;
+    this.dx = (this.destX - this.getX()) / ticks;
+    this.dy = (this.destY - this.getY()) / ticks;
+    this.destCounter = ticks;
+  };
+
+  this.move = function(x, y) {
+    this.x += this.dx;
+    this.y += this.dy;
+    this.onmove();
+    if (this.destCounter !== undefined) {
+      this.destCounter--;
+      if (this.destCounter <= 0) {
+        this.destCounter = undefined;
+        this.x = this.destX;
+        this.y = this.destY;
+        this.onreachdestination();
+      }
+    }
+  };
+
+  this.moveTo = function(x, y) {
+    this.x = x;
+    this.y = y;
+  };
+
+  this.moveCenterTo = function(x, y) {
+    this.x = x - this.getWidth() / 2;
+    this.y = y - this.getHeight() / 2;
+  };
+
+  this.containsPoint = function(x, y) {
+    return (x >= this.getX() && x <= this.getX() + this.getWidth() &&
+            y >= this.getY() && y <= this.getY() + this.getHeight());
+  };
+
+  // you will probably want to override this
+  this.draw = function(ctx) {
+    ctx.fillStyle = this.fillStyle || "green";
+    ctx.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
+  };
+
+  // event handlers.  override to detect these events.
+  this.ongrab = function() {
+  };
+  this.ondrag = function() {
+  };
+  this.ondrop = function() {
+  };
+  this.onclick = function() {
+  };
+  this.onmove = function() {
+  };
+  this.onreachdestination = function() {
+    this.setVelocity(0, 0);
+  };
+
+};
+
+/*
+ * This still has a few shortcomings at the moment.
+ */
+yoob.SpriteManager = function() {
+  this.canvas = undefined;
+  this.canvasX = undefined;
+  this.canvasY = undefined;
+  this.offsetX = undefined;
+  this.offsetY = undefined;
+  this.dragging = undefined;
+  this.sprites = [];
+
+  /*
+   * Common handling of mouse and touch events
+   */
+  this.onmousedown = function(e, touch) {
+    e.preventDefault();
+    this.canvasX = touch.pageX - this.canvas.offsetLeft;
+    this.canvasY = touch.pageY - this.canvas.offsetTop;
+
+    var sprite = this.getSpriteAt(this.canvasX, this.canvasY);
+    if (sprite === undefined) return;
+    if (sprite.isDraggable) {
+      this.dragging = sprite;
+      this.dragging.selected = true;
+      this.dragging.ongrab();
+      this.offsetX = sprite.getX() - this.canvasX;
+      this.offsetY = sprite.getY() - this.canvasY;
+      this.canvas.style.cursor = "move";
+    } else if (sprite.isClickable) {
+      sprite.onclick(e);
+    }
+  };
+
+  this.onmousemove = function(e, touch) {
+    e.preventDefault();
+    if (!this.dragging) return;
+
+    this.canvasX = touch.pageX - this.canvas.offsetLeft;
+    this.canvasY = touch.pageY - this.canvas.offsetTop;
+
+    this.dragging.moveTo(this.canvasX + this.offsetX,
+                         this.canvasY + this.offsetY);
+  };
+
+  this.onmouseup = function(e, touch) {
+    e.preventDefault();
+    this.canvas.onmousemove = null;
+    this.canvasX = undefined;
+    this.canvasY = undefined;
+    this.offsetX = undefined;
+    this.offsetY = undefined;
+    if (this.dragging !== undefined) {
+      this.dragging.ondrop();
+      this.dragging.selected = false;
+    }
+    this.dragging = undefined;
+    this.canvas.style.cursor = "auto";
+  };
+
+  /*
+   * Attach this SpriteManager to a canvas.
+   */
+  this.init = function(canvas) {
+    this.canvas = canvas;
+
+    var self = this;
+    this.canvas.addEventListener('mousedown', function(e) {
+      return self.onmousedown(e, e);
+    });
+    this.canvas.addEventListener('touchstart', function(e) {
+      return self.onmousedown(e, e.touches[0]);
+    });
+
+    this.canvas.addEventListener('mousemove', function(e) {
+      return self.onmousemove(e, e);
+    });
+    this.canvas.addEventListener('touchmove', function(e) {
+      return self.onmousemove(e, e.touches[0]);
+    });
+
+    this.canvas.addEventListener('mouseup', function(e) {
+      return self.onmouseup(e, e);
+    });
+    this.canvas.addEventListener('touchend', function(e) {
+      return self.onmouseup(e, e.touches[0]);
+    });
+  };
+
+  this.move = function() {
+    this.foreach(function(sprite) { sprite.move(); });
+  };
+
+  this.draw = function(ctx) {
+    if (ctx === undefined) {
+      ctx = this.canvas.getContext('2d');
+    }
+    for (var i = 0; i < this.sprites.length; i++) {
+      this.sprites[i].draw(ctx);
+    }
+  };
+
+  this.addSprite = function(sprite) {
+    this.sprites.push(sprite);
+  };
+
+  this.removeSprite = function(sprite) {
+    var index = undefined;
+    for (var i = 0; i < this.sprites.length; i++) {
+      if (this.sprites[i] === sprite) {
+        index = i;
+        break;
+      }
+    }
+    if (index !== undefined) {
+      this.sprites.splice(index, 1);
+    }
+  };
+
+  this.moveToFront = function(sprite) {
+    this.removeSprite(sprite);
+    this.sprites.push(sprite);
+  };
+
+  this.moveToBack = function(sprite) {
+    this.removeSprite(sprite);
+    this.sprites.unshift(sprite);
+  };
+
+  this.getSpriteAt = function(x, y) {
+    for (var i = this.sprites.length-1; i >= 0; i--) {
+      var sprite = this.sprites[i];
+      if (sprite.containsPoint(x, y)) {
+        return sprite;
+      }
+    }
+    return undefined;
+  };
+
+  this.foreach = function(fun) {
+    for (var i = this.sprites.length-1; i >= 0; i--) {
+      var sprite = this.sprites[i];
+      var result = fun(sprite);
+      if (result === 'remove') {
+        this.removeSprite(sprite);
+      }
+      if (result === 'return') {
+        return sprite;
+      }
+    }
+  };
+
+};