<div class="about">A <a href="http://piqnt.com/stage.js/">Stage.js</a> Example
- <a href="https://github.com/piqnt/stage.js/tree/master/example/game-tictactoe">Source</a></div>
* {
margin: 0;
padding: 0;
border: none;
}
body,html {
height: 100%;
width: 100%;
font-family: sans-serif;
}
body {
overflow: hidden;
}
.about,.hint {
position: absolute;
padding: 0.7em 1em;
background: rgba(240, 240, 240, 0.7);
}
.dark .about,.dark .hint {
background: rgba(32, 32, 32, 0.7);
}
.about {
right: 0.5em;
bottom: 0.5em;
}
.hint {
left: 0.5em;
bottom: 0.5em;
}
.loading {
margin: 10% 10%;
font-size: xx-large;
position: absolute;
}
a {
text-decoration: none;
color: blue;
}
a:hover {
text-decoration: underline;
}
.dark {
color: #ccc;
background: #222;
}
.dark a {
color: #fff;
}
canvas {
background: #fff;
}
.dark canvas {
background: #222;
}
(function(supported) {
var body = document.body;
var loading = document.createElement('class');
loading.className = 'loading';
if (supported) {
loading.innerHTML = 'Loading...';
loading.style.zIndex = -1;
} else {
loading.innerHTML = 'Please use a <a target="_blank" href="https://www.google.com/search?q=modern+browser">modern browser!';
loading.style.zIndex = 0;
}
body.insertBefore(loading, body.firstChild);
})(function() {
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
}());
var status = (function() {
var el = null;
return function(msg) {
if (el === null) {
var el = document.createElement('div');
el.style.position = 'absolute';
el.style.color = 'black';
el.style.background = 'white';
el.style.zIndex = 999;
el.style.top = '5px';
el.style.right = '5px';
el.style.padding = '1px 5px';
document.body.appendChild(el);
}
el.innerHTML = msg;
};
})();
/*
* Stage.js 0.6.6
* Copyright (c) 2015 Ali Shakiba, Piqnt LLC
* Available under the MIT license
* @license
*/
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Stage=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = require("../lib/");
require("../lib/canvas");
require("../lib/image");
require("../lib/anim");
require("../lib/str");
require("../lib/layout");
require("../lib/addon/tween");
module.exports.Mouse = require("../lib/addon/mouse");
module.exports.Math = require("../lib/util/math");
module.exports._extend = require("../lib/util/extend");
module.exports._create = require("../lib/util/create");
require("../lib/loader/web");
},{"../lib/":11,"../lib/addon/mouse":3,"../lib/addon/tween":4,"../lib/anim":5,"../lib/canvas":7,"../lib/image":10,"../lib/layout":12,"../lib/loader/web":13,"../lib/str":19,"../lib/util/create":22,"../lib/util/extend":24,"../lib/util/math":26}],2:[function(require,module,exports){
function _identity(x) {
return x;
}
var _cache = {};
var _modes = {};
var _easings = {};
function Easing(token) {
if (typeof token === "function") {
return token;
}
if (typeof token !== "string") {
return _identity;
}
var fn = _cache[token];
if (fn) {
return fn;
}
var match = /^(\w+)(-(in|out|in-out|out-in))?(\((.*)\))?$/i.exec(token);
if (!match || !match.length) {
return _identity;
}
easing = _easings[match[1]];
mode = _modes[match[3]];
params = match[5];
if (easing && easing.fn) {
fn = easing.fn;
} else if (easing && easing.fc) {
fn = easing.fc.apply(easing.fc, params && params.replace(/\s+/, "").split(","));
} else {
fn = _identity;
}
if (mode) {
fn = mode.fn(fn);
}
// TODO: It can be a memory leak with different `params`.
_cache[token] = fn;
return fn;
}
Easing.add = function(data) {
var names = (data.name || data.mode).split(/\s+/);
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (name) {
(data.name ? _easings : _modes)[name] = data;
}
}
};
Easing.add({
mode: "in",
fn: function(f) {
return f;
}
});
Easing.add({
mode: "out",
fn: function(f) {
return function(t) {
return 1 - f(1 - t);
};
}
});
Easing.add({
mode: "in-out",
fn: function(f) {
return function(t) {
return t < .5 ? f(2 * t) / 2 : 1 - f(2 * (1 - t)) / 2;
};
}
});
Easing.add({
mode: "out-in",
fn: function(f) {
return function(t) {
return t < .5 ? 1 - f(2 * (1 - t)) / 2 : f(2 * t) / 2;
};
}
});
Easing.add({
name: "linear",
fn: function(t) {
return t;
}
});
Easing.add({
name: "quad",
fn: function(t) {
return t * t;
}
});
Easing.add({
name: "cubic",
fn: function(t) {
return t * t * t;
}
});
Easing.add({
name: "quart",
fn: function(t) {
return t * t * t * t;
}
});
Easing.add({
name: "quint",
fn: function(t) {
return t * t * t * t * t;
}
});
Easing.add({
name: "sin sine",
fn: function(t) {
return 1 - Math.cos(t * Math.PI / 2);
}
});
Easing.add({
name: "exp",
fn: function(t) {
return t == 0 ? 0 : Math.pow(2, 10 * (t - 1));
}
});
Easing.add({
name: "circle circ",
fn: function(t) {
return 1 - Math.sqrt(1 - t * t);
}
});
Easing.add({
name: "bounce",
fn: function(t) {
return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
}
});
Easing.add({
name: "poly",
fc: function(e) {
return function(t) {
return Math.pow(t, e);
};
}
});
Easing.add({
name: "elastic",
fc: function(a, p) {
p = p || .45;
a = a || 1;
var s = p / (2 * Math.PI) * Math.asin(1 / a);
return function(t) {
return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p);
};
}
});
Easing.add({
name: "back",
fc: function(s) {
s = typeof s !== "undefined" ? s : 1.70158;
return function(t) {
return t * t * ((s + 1) * t - s);
};
}
});
module.exports = Easing;
},{}],3:[function(require,module,exports){
if (typeof DEBUG === "undefined") DEBUG = true;
require("../core")._load(function(stage, elem) {
Mouse.subscribe(stage, elem);
});
// TODO: capture mouse
Mouse.CLICK = "click";
Mouse.START = "touchstart mousedown";
Mouse.MOVE = "touchmove mousemove";
Mouse.END = "touchend mouseup";
Mouse.CANCEL = "touchcancel mousecancel";
Mouse.subscribe = function(stage, elem) {
if (stage.mouse) {
return;
}
stage.mouse = new Mouse(stage, elem);
// `click` events are synthesized from start/end events on same nodes
// `mousecancel` events are synthesized on blur or mouseup outside element
elem.addEventListener("touchstart", handleStart);
elem.addEventListener("touchend", handleEnd);
elem.addEventListener("touchmove", handleMove);
elem.addEventListener("touchcancel", handleCancel);
elem.addEventListener("mousedown", handleStart);
elem.addEventListener("mouseup", handleEnd);
elem.addEventListener("mousemove", handleMove);
document.addEventListener("mouseup", handleCancel);
window.addEventListener("blur", handleCancel);
var clicklist = [], cancellist = [];
function handleStart(event) {
event.preventDefault();
stage.mouse.locate(event);
// DEBUG && console.log('Mouse Start: ' + event.type + ' ' + mouse);
stage.mouse.publish(event.type, event);
stage.mouse.lookup("click", clicklist);
stage.mouse.lookup("mousecancel", cancellist);
}
function handleMove(event) {
event.preventDefault();
stage.mouse.locate(event);
stage.mouse.publish(event.type, event);
}
function handleEnd(event) {
event.preventDefault();
// up/end location is not available, last one is used instead
// DEBUG && console.log('Mouse End: ' + event.type + ' ' + mouse);
stage.mouse.publish(event.type, event);
if (clicklist.length) {
// DEBUG && console.log('Mouse Click: ' + clicklist.length);
stage.mouse.publish("click", event, clicklist);
}
cancellist.length = 0;
}
function handleCancel(event) {
if (cancellist.length) {
// DEBUG && console.log('Mouse Cancel: ' + event.type);
stage.mouse.publish("mousecancel", event, cancellist);
}
clicklist.length = 0;
}
};
function Mouse(stage, elem) {
if (!(this instanceof Mouse)) {
// old-style mouse subscription
return;
}
var ratio = stage.viewport().ratio || 1;
stage.on("viewport", function(size) {
ratio = size.ratio || ratio;
});
this.x = 0;
this.y = 0;
this.toString = function() {
return (this.x | 0) + "x" + (this.y | 0);
};
this.locate = function(event) {
locateElevent(elem, event, this);
this.x *= ratio;
this.y *= ratio;
};
this.lookup = function(type, collect) {
this.type = type;
this.root = stage;
this.event = null;
collect.length = 0;
this.collect = collect;
this.root.visit(this.visitor, this);
};
this.publish = function(type, event, targets) {
this.type = type;
this.root = stage;
this.event = event;
this.collect = false;
this.timeStamp = Date.now();
if (type !== "mousemove" && type !== "touchmove") {
DEBUG && console.log(this.type + " " + this);
}
if (targets) {
while (targets.length) if (this.visitor.end(targets.shift(), this)) break;
targets.length = 0;
} else {
this.root.visit(this.visitor, this);
}
};
this.visitor = {
reverse: true,
visible: true,
start: function(node, mouse) {
return !node._flag(mouse.type);
},
end: function(node, mouse) {
// mouse: event/collect, type, root
rel.raw = mouse.event;
rel.type = mouse.type;
rel.timeStamp = mouse.timeStamp;
rel.abs.x = mouse.x;
rel.abs.y = mouse.y;
var listeners = node.listeners(mouse.type);
if (!listeners) {
return;
}
node.matrix().inverse().map(mouse, rel);
if (!(node === mouse.root || node.hitTest(rel))) {
return;
}
if (mouse.collect) {
mouse.collect.push(node);
}
if (mouse.event) {
var cancel = false;
for (var l = 0; l < listeners.length; l++) {
cancel = listeners[l].call(node, rel) ? true : cancel;
}
return cancel;
}
}
};
}
// TODO: define per mouse object with get-only x and y
var rel = {}, abs = {};
defineValue(rel, "clone", function(obj) {
obj = obj || {}, obj.x = this.x, obj.y = this.y;
return obj;
});
defineValue(rel, "toString", function() {
return (this.x | 0) + "x" + (this.y | 0) + " (" + this.abs + ")";
});
defineValue(rel, "abs", abs);
defineValue(abs, "clone", function(obj) {
obj = obj || {}, obj.x = this.x, obj.y = this.y;
return obj;
});
defineValue(abs, "toString", function() {
return (this.x | 0) + "x" + (this.y | 0);
});
function defineValue(obj, name, value) {
Object.defineProperty(obj, name, {
value: value
});
}
function locateElevent(el, ev, loc) {
// pageX/Y if available?
if (ev.touches && ev.touches.length) {
loc.x = ev.touches[0].clientX;
loc.y = ev.touches[0].clientY;
} else {
loc.x = ev.clientX;
loc.y = ev.clientY;
}
var rect = el.getBoundingClientRect();
loc.x -= rect.left;
loc.y -= rect.top;
loc.x -= el.clientLeft | 0;
loc.y -= el.clientTop | 0;
return loc;
}
module.exports = Mouse;
},{"../core":8}],4:[function(require,module,exports){
var Easing = require("./easing");
var Class = require("../core");
var Pin = require("../pin");
Class.prototype.tween = function(duration, delay) {
if (!this._tween) {
this._tween = new Tween(this);
}
return this._tween.tween(duration, delay);
};
var iid = 0;
function Tween(node) {
// TODO: reuse head.start/keys/end
var tween = this;
this._owner = node;
this._queue = [];
this._next = null;
var lastTime = 0;
node.tick(function(elapsed, now, last) {
if (!tween._queue.length) {
return;
}
// ignore old elapsed
var ignore = lastTime != last;
lastTime = now;
if (ignore) {
return true;
}
var head = tween._queue[0];
if (!head.time) {
head.time = 1;
} else {
head.time += elapsed;
}
if (head.time < head.delay) {
return;
}
if (!head.start) {
head.start = {};
head.keys = {};
for (var key in head.end) {
if (typeof (start = node.pin(key)) === "number") {
head.start[key] = start;
head.keys[key] = key;
} else if (typeof (startX = node.pin(key + "X")) === "number" && typeof (startY = node.pin(key + "Y")) === "number") {
head.start[key + "X"] = startX;
head.keys[key + "X"] = key;
head.start[key + "Y"] = startY;
head.keys[key + "Y"] = key;
}
}
}
var p = (head.time - head.delay) / head.duration;
var over = p >= 1;
p = p > 1 ? 1 : p;
p = typeof head.easing == "function" ? head.easing(p) : p;
var q = 1 - p;
for (var key in head.keys) {
node.pin(key, head.start[key] * q + head.end[head.keys[key]] * p);
}
if (over) {
tween._queue.shift();
head.then && head.then.call(node);
}
}, true);
}
Tween.prototype.tween = function(duration, delay) {
this._next = {
id: ++iid,
end: {},
duration: duration || 400,
delay: delay || 0
};
return this;
};
Tween.prototype.pin = function(a, b) {
if (this._next !== this._queue[this._queue.length - 1]) {
this._owner.touch();
this._queue.push(this._next);
}
var end = this._next.end;
if (typeof a === "object") {
for (var attr in a) {
end[attr] = a[attr];
}
} else if (typeof b !== "undefined") {
end[a] = b;
}
return this;
};
Tween.prototype.clear = function(forward) {
var tween;
while (tween = this._queue.shift()) {
forward && this._owner.pin(tween.end);
}
return this;
};
Tween.prototype.then = function(then) {
this._next.then = then;
return this;
};
Tween.prototype.ease = function(easing) {
this._next.easing = Easing(easing);
return this;
};
Pin._add_shortcuts(Tween);
module.exports = Tween;
},{"../core":8,"../pin":16,"./easing":2}],5:[function(require,module,exports){
var Class = require("./core");
require("./pin");
require("./render");
var create = require("./util/create");
var math = require("./util/math");
Class.anim = function(frames, fps) {
var anim = new Anim();
anim.frames(frames).gotoFrame(0);
fps && anim.fps(fps);
return anim;
};
Anim._super = Class;
Anim.prototype = create(Anim._super.prototype);
// TODO: replace with atlas fps or texture time
Class.Anim = {
FPS: 15
};
function Anim() {
Anim._super.call(this);
this.label("Anim");
this._textures = [];
this._fps = Class.Anim.FPS;
this._ft = 1e3 / this._fps;
this._time = -1;
this._repeat = 0;
this._index = 0;
this._frames = [];
var lastTime = 0;
this.tick(function(t, now, last) {
if (this._time < 0 || this._frames.length <= 1) {
return;
}
// ignore old elapsed
var ignore = lastTime != last;
lastTime = now;
if (ignore) {
return true;
}
this._time += t;
if (this._time < this._ft) {
return true;
}
var n = this._time / this._ft | 0;
this._time -= n * this._ft;
this.moveFrame(n);
if (this._repeat > 0 && (this._repeat -= n) <= 0) {
this.stop();
this._callback && this._callback();
return false;
}
return true;
}, false);
}
Anim.prototype.fps = function(fps) {
if (typeof fps === "undefined") {
return this._fps;
}
this._fps = fps > 0 ? fps : Class.Anim.FPS;
this._ft = 1e3 / this._fps;
return this;
};
/**
* @deprecated Use frames
*/
Anim.prototype.setFrames = function(a, b, c) {
return this.frames(a, b, c);
};
Anim.prototype.frames = function(frames) {
this._index = 0;
this._frames = Class.texture(frames).array();
this.touch();
return this;
};
Anim.prototype.length = function() {
return this._frames ? this._frames.length : 0;
};
Anim.prototype.gotoFrame = function(frame, resize) {
this._index = math.rotate(frame, this._frames.length) | 0;
resize = resize || !this._textures[0];
this._textures[0] = this._frames[this._index];
if (resize) {
this.pin("width", this._textures[0].width);
this.pin("height", this._textures[0].height);
}
this.touch();
return this;
};
Anim.prototype.moveFrame = function(move) {
return this.gotoFrame(this._index + move);
};
Anim.prototype.repeat = function(repeat, callback) {
this._repeat = repeat * this._frames.length - 1;
this._callback = callback;
this.play();
return this;
};
Anim.prototype.play = function(frame) {
if (typeof frame !== "undefined") {
this.gotoFrame(frame);
this._time = 0;
} else if (this._time < 0) {
this._time = 0;
}
this.touch();
return this;
};
Anim.prototype.stop = function(frame) {
this._time = -1;
if (typeof frame !== "undefined") {
this.gotoFrame(frame);
}
return this;
};
},{"./core":8,"./pin":16,"./render":17,"./util/create":22,"./util/math":26}],6:[function(require,module,exports){
if (typeof DEBUG === "undefined") DEBUG = true;
var Class = require("./core");
var Texture = require("./texture");
var extend = require("./util/extend");
var create = require("./util/create");
var is = require("./util/is");
var string = require("./util/string");
// name : atlas
var _atlases_map = {};
// [atlas]
var _atlases_arr = [];
// TODO: print subquery not found error
// TODO: index textures
Class.atlas = function(def) {
var atlas = is.fn(def.draw) ? def : new Atlas(def);
if (def.name) {
_atlases_map[def.name] = atlas;
}
_atlases_arr.push(atlas);
deprecated(def, "imagePath");
deprecated(def, "imageRatio");
var url = def.imagePath;
var ratio = def.imageRatio || 1;
if (is.string(def.image)) {
url = def.image;
} else if (is.hash(def.image)) {
url = def.image.src || def.image.url;
ratio = def.image.ratio || ratio;
}
url && Class.preload(function(done) {
DEBUG && console.log("Loading atlas: " + url);
var imageloader = Class.config("image-loader");
imageloader(url, function(image) {
DEBUG && console.log("Image loaded: " + url);
atlas.src(image, ratio);
done();
}, function(err) {
DEBUG && console.log("Error loading atlas: " + url, err);
done();
});
});
return atlas;
};
Atlas._super = Texture;
Atlas.prototype = create(Atlas._super.prototype);
function Atlas(def) {
Atlas._super.call(this);
var atlas = this;
deprecated(def, "filter");
deprecated(def, "cutouts");
deprecated(def, "sprites");
deprecated(def, "factory");
var map = def.map || def.filter;
var ppu = def.ppu || def.ratio || 1;
var trim = def.trim || 0;
var textures = def.textures;
var factory = def.factory;
var cutouts = def.cutouts || def.sprites;
function make(def) {
if (!def || is.fn(def.draw)) {
return def;
}
def = extend({}, def);
if (is.fn(map)) {
def = map(def);
}
if (ppu != 1) {
def.x *= ppu, def.y *= ppu;
def.width *= ppu, def.height *= ppu;
def.top *= ppu, def.bottom *= ppu;
def.left *= ppu, def.right *= ppu;
}
if (trim != 0) {
def.x += trim, def.y += trim;
def.width -= 2 * trim, def.height -= 2 * trim;
def.top -= trim, def.bottom -= trim;
def.left -= trim, def.right -= trim;
}
var texture = atlas.pipe();
texture.top = def.top, texture.bottom = def.bottom;
texture.left = def.left, texture.right = def.right;
texture.src(def.x, def.y, def.width, def.height);
return texture;
}
function find(query) {
if (textures) {
if (is.fn(textures)) {
return textures(query);
} else if (is.hash(textures)) {
return textures[query];
}
}
if (cutouts) {
// deprecated
var result = null, n = 0;
for (var i = 0; i < cutouts.length; i++) {
if (string.startsWith(cutouts[i].name, query)) {
if (n === 0) {
result = cutouts[i];
} else if (n === 1) {
result = [ result, cutouts[i] ];
} else {
result.push(cutouts[i]);
}
n++;
}
}
if (n === 0 && is.fn(factory)) {
result = function(subquery) {
return factory(query + (subquery ? subquery : ""));
};
}
return result;
}
}
this.select = function(query) {
if (!query) {
// TODO: if `textures` is texture def, map or fn?
return new Selection(this.pipe());
}
var found = find(query);
if (found) {
return new Selection(found, find, make);
}
};
}
var nfTexture = new Texture();
nfTexture.x = nfTexture.y = nfTexture.width = nfTexture.height = 0;
nfTexture.pipe = nfTexture.src = nfTexture.dest = function() {
return this;
};
nfTexture.draw = function() {};
var nfSelection = new Selection(nfTexture);
function Selection(result, find, make) {
function link(result, subquery) {
if (!result) {
return nfTexture;
} else if (is.fn(result.draw)) {
return result;
} else if (is.hash(result) && is.number(result.width) && is.number(result.height) && is.fn(make)) {
return make(result);
} else if (is.hash(result) && is.defined(subquery)) {
return link(result[subquery]);
} else if (is.fn(result)) {
return link(result(subquery));
} else if (is.array(result)) {
return link(result[0]);
} else if (is.string(result) && is.fn(find)) {
return link(find(result));
}
}
this.one = function(subquery) {
return link(result, subquery);
};
this.array = function(arr) {
var array = is.array(arr) ? arr : [];
if (is.array(result)) {
for (var i = 0; i < result.length; i++) {
array[i] = link(result[i]);
}
} else {
array[0] = link(result);
}
return array;
};
}
Class.texture = function(query) {
if (!is.string(query)) {
return new Selection(query);
}
var result = null, atlas, i;
if ((i = query.indexOf(":")) > 0 && query.length > i + 1) {
atlas = _atlases_map[query.slice(0, i)];
result = atlas && atlas.select(query.slice(i + 1));
}
if (!result && (atlas = _atlases_map[query])) {
result = atlas.select();
}
for (i = 0; !result && i < _atlases_arr.length; i++) {
result = _atlases_arr[i].select(query);
}
if (!result) {
console.error("Texture not found: " + query);
result = nfSelection;
}
return result;
};
function deprecated(hash, name, msg) {
if (name in hash) console.log(msg ? msg.replace("%name", name) : "'" + name + "' field of texture atlas is deprecated.");
}
module.exports = Atlas;
},{"./core":8,"./texture":20,"./util/create":22,"./util/extend":24,"./util/is":25,"./util/string":30}],7:[function(require,module,exports){
var Class = require("./core");
var Texture = require("./texture");
Class.canvas = function(type, attributes, callback) {
if (typeof type === "string") {
if (typeof attributes === "object") {} else {
if (typeof attributes === "function") {
callback = attributes;
}
attributes = {};
}
} else {
if (typeof type === "function") {
callback = type;
}
attributes = {};
type = "2d";
}
var canvas = document.createElement("canvas");
var context = canvas.getContext(type, attributes);
var texture = new Texture(canvas);
texture.context = function() {
return context;
};
texture.size = function(width, height, ratio) {
ratio = ratio || 1;
canvas.width = width * ratio;
canvas.height = height * ratio;
this.src(canvas, ratio);
return this;
};
texture.canvas = function(fn) {
if (typeof fn === "function") {
fn.call(this, context);
} else if (typeof fn === "undefined" && typeof callback === "function") {
callback.call(this, context);
}
return this;
};
if (typeof callback === "function") {
callback.call(texture, context);
}
return texture;
};
},{"./core":8,"./texture":20}],8:[function(require,module,exports){
if (typeof DEBUG === "undefined") DEBUG = true;
var stats = require("./util/stats");
var extend = require("./util/extend");
var once = require("./util/once");
var is = require("./util/is");
var await = require("./util/await");
stats.create = 0;
function Class(arg) {
if (!(this instanceof Class)) {
if (is.fn(arg)) {
return Class.app.apply(Class, arguments);
} else if (is.object(arg)) {
return Class.atlas.apply(Class, arguments);
} else {
return arg;
}
}
stats.create++;
for (var i = 0; i < _init.length; i++) {
_init[i].call(this);
}
}
var _init = [];
Class._init = function(fn) {
_init.push(fn);
};
var _load = [];
Class._load = function(fn) {
_load.push(fn);
};
var _config = {};
Class.config = function() {
if (arguments.length === 1 && is.string(arguments[0])) {
return _config[arguments[0]];
}
if (arguments.length === 1 && is.object(arguments[0])) {
extend(_config, arguments[0]);
}
if (arguments.length === 2 && is.string(arguments[0])) {
_config[(arguments[0], arguments[1])];
}
};
var _app_queue = [];
var _preload_queue = [];
var _stages = [];
var _loaded = false;
var _paused = false;
Class.app = function(app, opts) {
if (!_loaded) {
_app_queue.push(arguments);
return;
}
DEBUG && console.log("Creating app...");
var loader = Class.config("app-loader");
loader(function(stage, canvas) {
DEBUG && console.log("Initing app...");
for (var i = 0; i < _load.length; i++) {
_load[i].call(this, stage, canvas);
}
app(stage, canvas);
_stages.push(stage);
DEBUG && console.log("Starting app...");
stage.start();
}, opts);
};
var loading = await();
Class.preload = function(load) {
if (typeof load === "string") {
var url = load;
if (/\.js($|\?|\#)/.test(url)) {
load = function(callback) {
loadScript(url, callback);
};
}
}
if (typeof load !== "function") {
return;
}
// if (!_started) {
// _preload_queue.push(load);
// return;
// }
load(loading());
};
Class.start = function(config) {
DEBUG && console.log("Starting...");
Class.config(config);
// DEBUG && console.log('Preloading...');
// _started = true;
// while (_preload_queue.length) {
// var load = _preload_queue.shift();
// load(loading());
// }
loading.then(function() {
DEBUG && console.log("Loading apps...");
_loaded = true;
while (_app_queue.length) {
var args = _app_queue.shift();
Class.app.apply(Class, args);
}
});
};
Class.pause = function() {
if (!_paused) {
_paused = true;
for (var i = _stages.length - 1; i >= 0; i--) {
_stages[i].pause();
}
}
};
Class.resume = function() {
if (_paused) {
_paused = false;
for (var i = _stages.length - 1; i >= 0; i--) {
_stages[i].resume();
}
}
};
Class.create = function() {
return new Class();
};
module.exports = Class;
function loadScript(src, callback) {
var el = document.createElement("script");
el.addEventListener("load", function() {
callback();
});
el.addEventListener("error", function(err) {
callback(err || "Error loading script: " + src);
});
el.src = src;
el.id = "preload-" + Date.now();
document.body.appendChild(el);
}
},{"./util/await":21,"./util/extend":24,"./util/is":25,"./util/once":27,"./util/stats":29}],9:[function(require,module,exports){
require("./util/event")(require("./core").prototype, function(obj, name, on) {
obj._flag(name, on);
});
},{"./core":8,"./util/event":23}],10:[function(require,module,exports){
var Class = require("./core");
require("./pin");
require("./render");
var repeat = require("./util/repeat");
var create = require("./util/create");
Class.image = function(image) {
var img = new Image();
image && img.image(image);
return img;
};
Image._super = Class;
Image.prototype = create(Image._super.prototype);
function Image() {
Image._super.call(this);
this.label("Image");
this._textures = [];
this._image = null;
}
/**
* @deprecated Use image
*/
Image.prototype.setImage = function(a, b, c) {
return this.image(a, b, c);
};
Image.prototype.image = function(image) {
this._image = Class.texture(image).one();
this.pin("width", this._image ? this._image.width : 0);
this.pin("height", this._image ? this._image.height : 0);
this._textures[0] = this._image.pipe();
this._textures.length = 1;
return this;
};
Image.prototype.tile = function(inner) {
this._repeat(false, inner);
return this;
};
Image.prototype.stretch = function(inner) {
this._repeat(true, inner);
return this;
};
Image.prototype._repeat = function(stretch, inner) {
var self = this;
this.untick(this._repeatTicker);
this.tick(this._repeatTicker = function() {
if (this._mo_stretch == this._pin._ts_transform) {
return;
}
this._mo_stretch = this._pin._ts_transform;
var width = this.pin("width");
var height = this.pin("height");
this._textures.length = repeat(this._image, width, height, stretch, inner, insert);
});
function insert(i, sx, sy, sw, sh, dx, dy, dw, dh) {
var repeat = self._textures.length > i ? self._textures[i] : self._textures[i] = self._image.pipe();
repeat.src(sx, sy, sw, sh);
repeat.dest(dx, dy, dw, dh);
}
};
},{"./core":8,"./pin":16,"./render":17,"./util/create":22,"./util/repeat":28}],11:[function(require,module,exports){
module.exports = require("./core");
module.exports.Matrix = require("./matrix");
module.exports.Texture = require("./texture");
require("./atlas");
require("./node");
require("./event");
require("./pin");
require("./render");
require("./root");
},{"./atlas":6,"./core":8,"./event":9,"./matrix":14,"./node":15,"./pin":16,"./render":17,"./root":18,"./texture":20}],12:[function(require,module,exports){
var Class = require("./core");
require("./pin");
require("./render");
var create = require("./util/create");
Class.row = function(align) {
return Class.create().row(align).label("Row");
};
Class.prototype.row = function(align) {
this.sequence("row", align);
return this;
};
Class.column = function(align) {
return Class.create().column(align).label("Row");
};
Class.prototype.column = function(align) {
this.sequence("column", align);
return this;
};
Class.sequence = function(type, align) {
return Class.create().sequence(type, align).label("Sequence");
};
Class.prototype.sequence = function(type, align) {
this._padding = this._padding || 0;
this._spacing = this._spacing || 0;
this.untick(this._layoutTiker);
this.tick(this._layoutTiker = function() {
if (this._mo_seq == this._ts_touch) {
return;
}
this._mo_seq = this._ts_touch;
var alignChildren = this._mo_seqAlign != this._ts_children;
this._mo_seqAlign = this._ts_children;
var width = 0, height = 0;
var child, next = this.first(true);
var first = true;
while (child = next) {
next = child.next(true);
child._pin.relativeMatrix();
var w = child.pin("boxWidth");
var h = child.pin("boxHeight");
if (type == "column") {
!first && (height += this._spacing);
child.pin("offsetY") != height && child.pin("offsetY", height);
width = Math.max(width, w);
height = height + h;
alignChildren && child.pin("alignX", align);
} else if (type == "row") {
!first && (width += this._spacing);
child.pin("offsetX") != width && child.pin("offsetX", width);
width = width + w;
height = Math.max(height, h);
alignChildren && child.pin("alignY", align);
}
first = false;
}
width += 2 * this._padding;
height += 2 * this._padding;
this.pin("width") != width && this.pin("width", width);
this.pin("height") != height && this.pin("height", height);
});
return this;
};
Class.box = function() {
return Class.create().box().label("Box");
};
Class.prototype.box = function() {
this._padding = this._padding || 0;
this.untick(this._layoutTiker);
this.tick(this._layoutTiker = function() {
if (this._mo_box == this._ts_touch) {
return;
}
this._mo_box = this._ts_touch;
var width = 0, height = 0;
var child, next = this.first(true);
while (child = next) {
next = child.next(true);
child._pin.relativeMatrix();
var w = child.pin("boxWidth");
var h = child.pin("boxHeight");
width = Math.max(width, w);
height = Math.max(height, h);
}
width += 2 * this._padding;
height += 2 * this._padding;
this.pin("width") != width && this.pin("width", width);
this.pin("height") != height && this.pin("height", height);
});
return this;
};
Class.layer = function() {
return Class.create().layer().label("Layer");
};
Class.prototype.layer = function() {
this.untick(this._layoutTiker);
this.tick(this._layoutTiker = function() {
var parent = this.parent();
if (parent) {
var width = parent.pin("width");
if (this.pin("width") != width) {
this.pin("width", width);
}
var height = parent.pin("height");
if (this.pin("height") != height) {
this.pin("height", height);
}
}
}, true);
return this;
};
// TODO: move padding to pin
Class.prototype.padding = function(pad) {
this._padding = pad;
return this;
};
Class.prototype.spacing = function(space) {
this._spacing = space;
return this;
};
},{"./core":8,"./pin":16,"./render":17,"./util/create":22}],13:[function(require,module,exports){
/**
* Default loader for web.
*/
if (typeof DEBUG === "undefined") DEBUG = true;
var Class = require("../core");
window.addEventListener("load", function() {
DEBUG && console.log("On load.");
Class.start();
}, false);
Class.config({
"app-loader": AppLoader,
"image-loader": ImageLoader
});
function AppLoader(app, configs) {
configs = configs || {};
var canvas = configs.canvas, context = null, full = false;
var width = 0, height = 0, ratio = 1;
if (typeof canvas === "string") {
canvas = document.getElementById(canvas);
}
if (!canvas) {
canvas = document.getElementById("cutjs") || document.getElementById("stage");
}
if (!canvas) {
full = true;
DEBUG && console.log("Creating Canvas...");
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
var body = document.body;
body.insertBefore(canvas, body.firstChild);
}
context = canvas.getContext("2d");
var devicePixelRatio = window.devicePixelRatio || 1;
var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
ratio = devicePixelRatio / backingStoreRatio;
var requestAnimationFrame = window.requestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame || function(callback) {
return window.setTimeout(callback, 1e3 / 60);
};
DEBUG && console.log("Creating stage...");
var root = Class.root(requestAnimationFrame, render);
function render() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, width, height);
root.render(context);
}
root.background = function(color) {
canvas.style.backgroundColor = color;
return this;
};
app(root, canvas);
resize();
window.addEventListener("resize", resize, false);
window.addEventListener("orientationchange", resize, false);
function resize() {
if (full) {
// screen.availWidth/Height?
width = window.innerWidth > 0 ? window.innerWidth : screen.width;
height = window.innerHeight > 0 ? window.innerHeight : screen.height;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
} else {
width = canvas.clientWidth;
height = canvas.clientHeight;
}
width *= ratio;
height *= ratio;
if (canvas.width === width && canvas.height === height) {
return;
}
canvas.width = width;
canvas.height = height;
DEBUG && console.log("Resize: " + width + " x " + height + " / " + ratio);
root.viewport(width, height, ratio);
render();
}
}
function ImageLoader(src, success, error) {
DEBUG && console.log("Loading image: " + src);
var image = new Image();
image.onload = function() {
success(image);
};
image.onerror = error;
image.src = src;
}
},{"../core":8}],14:[function(require,module,exports){
function Matrix(a, b, c, d, e, f) {
this.reset(a, b, c, d, e, f);
}
Matrix.prototype.toString = function() {
return "[" + this.a + ", " + this.b + ", " + this.c + ", " + this.d + ", " + this.e + ", " + this.f + "]";
};
Matrix.prototype.clone = function() {
return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
};
Matrix.prototype.reset = function(a, b, c, d, e, f) {
this._dirty = true;
if (typeof a === "object") {
this.a = a.a, this.d = a.d;
this.b = a.b, this.c = a.c;
this.e = a.e, this.f = a.f;
} else {
this.a = a || 1, this.d = d || 1;
this.b = b || 0, this.c = c || 0;
this.e = e || 0, this.f = f || 0;
}
return this;
};
Matrix.prototype.identity = function() {
this._dirty = true;
this.a = 1;
this.b = 0;
this.c = 0;
this.d = 1;
this.e = 0;
this.f = 0;
return this;
};
Matrix.prototype.rotate = function(angle) {
if (!angle) {
return this;
}
this._dirty = true;
var u = angle ? Math.cos(angle) : 1;
// android bug may give bad 0 values
var v = angle ? Math.sin(angle) : 0;
var a = u * this.a - v * this.b;
var b = u * this.b + v * this.a;
var c = u * this.c - v * this.d;
var d = u * this.d + v * this.c;
var e = u * this.e - v * this.f;
var f = u * this.f + v * this.e;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
return this;
};
Matrix.prototype.translate = function(x, y) {
if (!x && !y) {
return this;
}
this._dirty = true;
this.e += x;
this.f += y;
return this;
};
Matrix.prototype.scale = function(x, y) {
if (!(x - 1) && !(y - 1)) {
return this;
}
this._dirty = true;
this.a *= x;
this.b *= y;
this.c *= x;
this.d *= y;
this.e *= x;
this.f *= y;
return this;
};
Matrix.prototype.skew = function(x, y) {
if (!x && !y) {
return this;
}
this._dirty = true;
var a = this.a + this.b * x;
var b = this.b + this.a * y;
var c = this.c + this.d * x;
var d = this.d + this.c * y;
var e = this.e + this.f * x;
var f = this.f + this.e * y;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
return this;
};
Matrix.prototype.concat = function(m) {
this._dirty = true;
var n = this;
var a = n.a * m.a + n.b * m.c;
var b = n.b * m.d + n.a * m.b;
var c = n.c * m.a + n.d * m.c;
var d = n.d * m.d + n.c * m.b;
var e = n.e * m.a + m.e + n.f * m.c;
var f = n.f * m.d + m.f + n.e * m.b;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
return this;
};
Matrix.prototype.inverse = Matrix.prototype.reverse = function() {
if (this._dirty) {
this._dirty = false;
this.inversed = this.inversed || new Matrix();
var z = this.a * this.d - this.b * this.c;
this.inversed.a = this.d / z;
this.inversed.b = -this.b / z;
this.inversed.c = -this.c / z;
this.inversed.d = this.a / z;
this.inversed.e = (this.c * this.f - this.e * this.d) / z;
this.inversed.f = (this.e * this.b - this.a * this.f) / z;
}
return this.inversed;
};
Matrix.prototype.map = function(p, q) {
q = q || {};
q.x = this.a * p.x + this.c * p.y + this.e;
q.y = this.b * p.x + this.d * p.y + this.f;
return q;
};
Matrix.prototype.mapX = function(x, y) {
if (typeof x === "object") y = x.y, x = x.x;
return this.a * x + this.c * y + this.e;
};
Matrix.prototype.mapY = function(x, y) {
if (typeof x === "object") y = x.y, x = x.x;
return this.b * x + this.d * y + this.f;
};
module.exports = Matrix;
},{}],15:[function(require,module,exports){
var Class = require("./core");
var is = require("./util/is");
var iid = 0;
// TODO: do not clear next/prev/parent on remove
Class.prototype._label = "";
Class.prototype._visible = true;
Class.prototype._parent = null;
Class.prototype._next = null;
Class.prototype._prev = null;
Class.prototype._first = null;
Class.prototype._last = null;
Class.prototype._attrs = null;
Class.prototype._flags = null;
Class.prototype.toString = function() {
return "[" + this._label + "]";
};
/**
* @deprecated Use label()
*/
Class.prototype.id = function(id) {
return this.label(id);
};
Class.prototype.label = function(label) {
if (typeof label === "undefined") {
return this._label;
}
this._label = label;
return this;
};
Class.prototype.attr = function(name, value) {
if (typeof value === "undefined") {
return this._attrs !== null ? this._attrs[name] : undefined;
}
(this._attrs !== null ? this._attrs : this._attrs = {})[name] = value;
return this;
};
Class.prototype.visible = function(visible) {
if (typeof visible === "undefined") {
return this._visible;
}
this._visible = visible;
this._parent && (this._parent._ts_children = ++iid);
this._ts_pin = ++iid;
this.touch();
return this;
};
Class.prototype.hide = function() {
return this.visible(false);
};
Class.prototype.show = function() {
return this.visible(true);
};
Class.prototype.parent = function() {
return this._parent;
};
Class.prototype.next = function(visible) {
var next = this._next;
while (next && visible && !next._visible) {
next = next._next;
}
return next;
};
Class.prototype.prev = function(visible) {
var prev = this._prev;
while (prev && visible && !prev._visible) {
prev = prev._prev;
}
return prev;
};
Class.prototype.first = function(visible) {
var next = this._first;
while (next && visible && !next._visible) {
next = next._next;
}
return next;
};
Class.prototype.last = function(visible) {
var prev = this._last;
while (prev && visible && !prev._visible) {
prev = prev._prev;
}
return prev;
};
Class.prototype.visit = function(visitor, data) {
var reverse = visitor.reverse;
var visible = visitor.visible;
if (visitor.start && visitor.start(this, data)) {
return;
}
var child, next = reverse ? this.last(visible) : this.first(visible);
while (child = next) {
next = reverse ? child.prev(visible) : child.next(visible);
if (child.visit(visitor, data)) {
return true;
}
}
return visitor.end && visitor.end(this, data);
};
Class.prototype.append = function(child, more) {
if (is.array(child)) for (var i = 0; i < child.length; i++) append(this, child[i]); else if (typeof more !== "undefined") // deprecated
for (var i = 0; i < arguments.length; i++) append(this, arguments[i]); else if (typeof child !== "undefined") append(this, child);
return this;
};
Class.prototype.prepend = function(child, more) {
if (is.array(child)) for (var i = child.length - 1; i >= 0; i--) prepend(this, child[i]); else if (typeof more !== "undefined") // deprecated
for (var i = arguments.length - 1; i >= 0; i--) prepend(this, arguments[i]); else if (typeof child !== "undefined") prepend(this, child);
return this;
};
Class.prototype.appendTo = function(parent) {
append(parent, this);
return this;
};
Class.prototype.prependTo = function(parent) {
prepend(parent, this);
return this;
};
Class.prototype.insertNext = function(sibling, more) {
if (is.array(sibling)) for (var i = 0; i < sibling.length; i++) insertAfter(sibling[i], this); else if (typeof more !== "undefined") // deprecated
for (var i = 0; i < arguments.length; i++) insertAfter(arguments[i], this); else if (typeof sibling !== "undefined") insertAfter(sibling, this);
return this;
};
Class.prototype.insertPrev = function(sibling, more) {
if (is.array(sibling)) for (var i = sibling.length - 1; i >= 0; i--) insertBefore(sibling[i], this); else if (typeof more !== "undefined") // deprecated
for (var i = arguments.length - 1; i >= 0; i--) insertBefore(arguments[i], this); else if (typeof sibling !== "undefined") insertBefore(sibling, this);
return this;
};
Class.prototype.insertAfter = function(prev) {
insertAfter(this, prev);
return this;
};
Class.prototype.insertBefore = function(next) {
insertBefore(this, next);
return this;
};
function append(parent, child) {
_ensure(child);
_ensure(parent);
child.remove();
if (parent._last) {
parent._last._next = child;
child._prev = parent._last;
}
child._parent = parent;
parent._last = child;
if (!parent._first) {
parent._first = child;
}
child._parent._flag(child, true);
child._ts_parent = ++iid;
parent._ts_children = ++iid;
parent.touch();
}
function prepend(parent, child) {
_ensure(child);
_ensure(parent);
child.remove();
if (parent._first) {
parent._first._prev = child;
child._next = parent._first;
}
child._parent = parent;
parent._first = child;
if (!parent._last) {
parent._last = child;
}
child._parent._flag(child, true);
child._ts_parent = ++iid;
parent._ts_children = ++iid;
parent.touch();
}
function insertBefore(self, next) {
_ensure(self);
_ensure(next);
self.remove();
var parent = next._parent;
var prev = next._prev;
next._prev = self;
prev && (prev._next = self) || parent && (parent._first = self);
self._parent = parent;
self._prev = prev;
self._next = next;
self._parent._flag(self, true);
self._ts_parent = ++iid;
self.touch();
}
function insertAfter(self, prev) {
_ensure(self);
_ensure(prev);
self.remove();
var parent = prev._parent;
var next = prev._next;
prev._next = self;
next && (next._prev = self) || parent && (parent._last = self);
self._parent = parent;
self._prev = prev;
self._next = next;
self._parent._flag(self, true);
self._ts_parent = ++iid;
self.touch();
}
Class.prototype.remove = function(child, more) {
if (typeof child !== "undefined") {
if (is.array(child)) {
for (var i = 0; i < child.length; i++) _ensure(child[i]).remove();
} else if (typeof more !== "undefined") {
for (var i = 0; i < arguments.length; i++) _ensure(arguments[i]).remove();
} else {
_ensure(child).remove();
}
return this;
}
if (this._prev) {
this._prev._next = this._next;
}
if (this._next) {
this._next._prev = this._prev;
}
if (this._parent) {
if (this._parent._first === this) {
this._parent._first = this._next;
}
if (this._parent._last === this) {
this._parent._last = this._prev;
}
this._parent._flag(this, false);
this._parent._ts_children = ++iid;
this._parent.touch();
}
this._prev = this._next = this._parent = null;
this._ts_parent = ++iid;
// this._parent.touch();
return this;
};
Class.prototype.empty = function() {
var child, next = this._first;
while (child = next) {
next = child._next;
child._prev = child._next = child._parent = null;
this._flag(child, false);
}
this._first = this._last = null;
this._ts_children = ++iid;
this.touch();
return this;
};
Class.prototype.touch = function() {
this._ts_touch = ++iid;
this._parent && this._parent.touch();
return this;
};
/**
* Deep flags used for optimizing event distribution.
*/
Class.prototype._flag = function(obj, name) {
if (typeof name === "undefined") {
return this._flags !== null && this._flags[obj] || 0;
}
if (typeof obj === "string") {
if (name) {
this._flags = this._flags || {};
if (!this._flags[obj] && this._parent) {
this._parent._flag(obj, true);
}
this._flags[obj] = (this._flags[obj] || 0) + 1;
} else if (this._flags && this._flags[obj] > 0) {
if (this._flags[obj] == 1 && this._parent) {
this._parent._flag(obj, false);
}
this._flags[obj] = this._flags[obj] - 1;
}
}
if (typeof obj === "object") {
if (obj._flags) {
for (var type in obj._flags) {
if (obj._flags[type] > 0) {
this._flag(type, name);
}
}
}
}
return this;
};
/**
* @private
*/
Class.prototype.hitTest = function(hit) {
if (this.attr("spy")) {
return true;
}
return hit.x >= 0 && hit.x <= this._pin._width && hit.y >= 0 && hit.y <= this._pin._height;
};
function _ensure(obj) {
if (obj && obj instanceof Class) {
return obj;
}
throw "Invalid node: " + obj;
}
module.exports = Class;
},{"./core":8,"./util/is":25}],16:[function(require,module,exports){
var Class = require("./core");
var Matrix = require("./matrix");
var iid = 0;
Class._init(function() {
this._pin = new Pin(this);
});
Class.prototype.matrix = function() {
return this._pin.absoluteMatrix();
};
Class.prototype.pin = function(a, b) {
if (typeof a === "object") {
this._pin.set(a);
return this;
} else if (typeof a === "string") {
if (typeof b === "undefined") {
return this._pin.get(a);
} else {
this._pin.set(a, b);
return this;
}
} else if (typeof a === "undefined") {
return this._pin;
}
};
function Pin(owner) {
this._owner = owner;
this._parent = null;
// relative to parent
this._relativeMatrix = new Matrix();
// relative to stage
this._absoluteMatrix = new Matrix();
this.reset();
}
Pin.prototype.reset = function() {
this._textureAlpha = 1;
this._alpha = 1;
this._width = 0;
this._height = 0;
this._scaleX = 1;
this._scaleY = 1;
this._skewX = 0;
this._skewY = 0;
this._rotation = 0;
// scale/skew/rotate center
this._pivoted = false;
this._pivotX = null;
this._pivotY = null;
// self pin point
this._handled = false;
this._handleX = 0;
this._handleY = 0;
// parent pin point
this._aligned = false;
this._alignX = 0;
this._alignY = 0;
// as seen by parent px
this._offsetX = 0;
this._offsetY = 0;
this._boxX = 0;
this._boxY = 0;
this._boxWidth = this._width;
this._boxHeight = this._height;
// TODO: also set for owner
this._ts_translate = ++iid;
this._ts_transform = ++iid;
this._ts_matrix = ++iid;
};
Pin.prototype._update = function() {
this._parent = this._owner._parent && this._owner._parent._pin;
// if handled and transformed then be translated
if (this._handled && this._mo_handle != this._ts_transform) {
this._mo_handle = this._ts_transform;
this._ts_translate = ++iid;
}
if (this._aligned && this._parent && this._mo_align != this._parent._ts_transform) {
this._mo_align = this._parent._ts_transform;
this._ts_translate = ++iid;
}
return this;
};
Pin.prototype.toString = function() {
return this._owner + " (" + (this._parent ? this._parent._owner : null) + ")";
};
// TODO: ts fields require refactoring
Pin.prototype.absoluteMatrix = function() {
this._update();
var ts = Math.max(this._ts_transform, this._ts_translate, this._parent ? this._parent._ts_matrix : 0);
if (this._mo_abs == ts) {
return this._absoluteMatrix;
}
this._mo_abs = ts;
var abs = this._absoluteMatrix;
abs.reset(this.relativeMatrix());
this._parent && abs.concat(this._parent._absoluteMatrix);
this._ts_matrix = ++iid;
return abs;
};
Pin.prototype.relativeMatrix = function() {
this._update();
var ts = Math.max(this._ts_transform, this._ts_translate, this._parent ? this._parent._ts_transform : 0);
if (this._mo_rel == ts) {
return this._relativeMatrix;
}
this._mo_rel = ts;
var rel = this._relativeMatrix;
rel.identity();
if (this._pivoted) {
rel.translate(-this._pivotX * this._width, -this._pivotY * this._height);
}
rel.scale(this._scaleX, this._scaleY);
rel.skew(this._skewX, this._skewY);
rel.rotate(this._rotation);
if (this._pivoted) {
rel.translate(this._pivotX * this._width, this._pivotY * this._height);
}
// calculate effective box
if (this._pivoted) {
// origin
this._boxX = 0;
this._boxY = 0;
this._boxWidth = this._width;
this._boxHeight = this._height;
} else {
// aabb
var p, q;
if (rel.a > 0 && rel.c > 0 || rel.a < 0 && rel.c < 0) {
p = 0, q = rel.a * this._width + rel.c * this._height;
} else {
p = rel.a * this._width, q = rel.c * this._height;
}
if (p > q) {
this._boxX = q;
this._boxWidth = p - q;
} else {
this._boxX = p;
this._boxWidth = q - p;
}
if (rel.b > 0 && rel.d > 0 || rel.b < 0 && rel.d < 0) {
p = 0, q = rel.b * this._width + rel.d * this._height;
} else {
p = rel.b * this._width, q = rel.d * this._height;
}
if (p > q) {
this._boxY = q;
this._boxHeight = p - q;
} else {
this._boxY = p;
this._boxHeight = q - p;
}
}
this._x = this._offsetX;
this._y = this._offsetY;
this._x -= this._boxX + this._handleX * this._boxWidth;
this._y -= this._boxY + this._handleY * this._boxHeight;
if (this._aligned && this._parent) {
this._parent.relativeMatrix();
this._x += this._alignX * this._parent._width;
this._y += this._alignY * this._parent._height;
}
rel.translate(this._x, this._y);
return this._relativeMatrix;
};
Pin.prototype.get = function(a) {
return Pin._get(this, a);
};
// TODO: Use get/set or defineProperty instead?
Pin.prototype.set = function(a, b) {
if (typeof a === "string") {
Pin._set(this, a, b);
} else if (typeof a === "object") {
for (b in a) Pin._set(this, b, a[b], a);
}
if (this._owner) {
this._owner._ts_pin = ++iid;
this._owner.touch();
}
return this;
};
Pin._get = function(pin, key) {
if (typeof (key = Pin._getters[key]) !== "undefined") {
return key.call(Pin._getters, pin);
}
};
Pin._set = function(pin, key, value, all) {
if (typeof (key = Pin._setters[key]) !== "undefined" && typeof value !== "undefined") {
key.call(Pin._setters, pin, value, all);
}
};
Pin._getters = {
alpha: function(pin) {
return pin._alpha;
},
textureAlpha: function(pin) {
return pin._textureAlpha;
},
width: function(pin) {
return pin._width;
},
height: function(pin) {
return pin._height;
},
boxWidth: function(pin) {
return pin._boxWidth;
},
boxHeight: function(pin) {
return pin._boxHeight;
},
// scale : function(pin) {
// },
scaleX: function(pin) {
return pin._scaleX;
},
scaleY: function(pin) {
return pin._scaleY;
},
// skew : function(pin) {
// },
skewX: function(pin) {
return pin._skewX;
},
skewY: function(pin) {
return pin._skewY;
},
rotation: function(pin) {
return pin._rotation;
},
// pivot : function(pin) {
// },
pivotX: function(pin) {
return pin._pivotX;
},
pivotY: function(pin) {
return pin._pivotY;
},
// offset : function(pin) {
// },
offsetX: function(pin) {
return pin._offsetX;
},
offsetY: function(pin) {
return pin._offsetY;
},
// align : function(pin) {
// },
alignX: function(pin) {
return pin._alignX;
},
alignY: function(pin) {
return pin._alignY;
},
// handle : function(pin) {
// },
handleX: function(pin) {
return pin._handleX;
},
handleY: function(pin) {
return pin._handleY;
}
};
Pin._setters = {
alpha: function(pin, value) {
pin._alpha = value;
},
textureAlpha: function(pin, value) {
pin._textureAlpha = value;
},
width: function(pin, value) {
pin._width_ = value;
pin._width = value;
pin._ts_transform = ++iid;
},
height: function(pin, value) {
pin._height_ = value;
pin._height = value;
pin._ts_transform = ++iid;
},
scale: function(pin, value) {
pin._scaleX = value;
pin._scaleY = value;
pin._ts_transform = ++iid;
},
scaleX: function(pin, value) {
pin._scaleX = value;
pin._ts_transform = ++iid;
},
scaleY: function(pin, value) {
pin._scaleY = value;
pin._ts_transform = ++iid;
},
skew: function(pin, value) {
pin._skewX = value;
pin._skewY = value;
pin._ts_transform = ++iid;
},
skewX: function(pin, value) {
pin._skewX = value;
pin._ts_transform = ++iid;
},
skewY: function(pin, value) {
pin._skewY = value;
pin._ts_transform = ++iid;
},
rotation: function(pin, value) {
pin._rotation = value;
pin._ts_transform = ++iid;
},
pivot: function(pin, value) {
pin._pivotX = value;
pin._pivotY = value;
pin._pivoted = true;
pin._ts_transform = ++iid;
},
pivotX: function(pin, value) {
pin._pivotX = value;
pin._pivoted = true;
pin._ts_transform = ++iid;
},
pivotY: function(pin, value) {
pin._pivotY = value;
pin._pivoted = true;
pin._ts_transform = ++iid;
},
offset: function(pin, value) {
pin._offsetX = value;
pin._offsetY = value;
pin._ts_translate = ++iid;
},
offsetX: function(pin, value) {
pin._offsetX = value;
pin._ts_translate = ++iid;
},
offsetY: function(pin, value) {
pin._offsetY = value;
pin._ts_translate = ++iid;
},
align: function(pin, value) {
this.alignX(pin, value);
this.alignY(pin, value);
},
alignX: function(pin, value) {
pin._alignX = value;
pin._aligned = true;
pin._ts_translate = ++iid;
this.handleX(pin, value);
},
alignY: function(pin, value) {
pin._alignY = value;
pin._aligned = true;
pin._ts_translate = ++iid;
this.handleY(pin, value);
},
handle: function(pin, value) {
this.handleX(pin, value);
this.handleY(pin, value);
},
handleX: function(pin, value) {
pin._handleX = value;
pin._handled = true;
pin._ts_translate = ++iid;
},
handleY: function(pin, value) {
pin._handleY = value;
pin._handled = true;
pin._ts_translate = ++iid;
},
resizeMode: function(pin, value, all) {
if (all) {
if (value == "in") {
value = "in-pad";
} else if (value == "out") {
value = "out-crop";
}
pin._scaleTo(all.resizeWidth, all.resizeHeight, value);
}
},
resizeWidth: function(pin, value, all) {
if (!all || !all.resizeMode) {
pin._scaleTo(value, null);
}
},
resizeHeight: function(pin, value, all) {
if (!all || !all.resizeMode) {
pin._scaleTo(null, value);
}
},
scaleMode: function(pin, value, all) {
if (all) {
pin._scaleTo(all.scaleWidth, all.scaleHeight, value);
}
},
scaleWidth: function(pin, value, all) {
if (!all || !all.scaleMode) {
pin._scaleTo(value, null);
}
},
scaleHeight: function(pin, value, all) {
if (!all || !all.scaleMode) {
pin._scaleTo(null, value);
}
},
matrix: function(pin, value) {
this.scaleX(pin, value.a);
this.skewX(pin, value.c / value.d);
this.skewY(pin, value.b / value.a);
this.scaleY(pin, value.d);
this.offsetX(pin, value.e);
this.offsetY(pin, value.f);
this.rotation(pin, 0);
}
};
Pin.prototype._scaleTo = function(width, height, mode) {
var w = typeof width === "number";
var h = typeof height === "number";
var m = typeof mode === "string";
var pin = this;
pin._ts_transform = ++iid;
if (w) {
pin._scaleX = width / pin._width_;
pin._width = pin._width_;
}
if (h) {
pin._scaleY = height / pin._height_;
pin._height = pin._height_;
}
if (w && h && m) {
if (mode == "out" || mode == "out-crop") {
pin._scaleX = pin._scaleY = Math.max(pin._scaleX, pin._scaleY);
} else if (mode == "in" || mode == "in-pad") {
pin._scaleX = pin._scaleY = Math.min(pin._scaleX, pin._scaleY);
}
if (mode == "out-crop" || mode == "in-pad") {
pin._width = width / pin._scaleX;
pin._height = height / pin._scaleY;
}
}
};
// Used by Tween class.
Pin._add_shortcuts = function(Class) {
Class.prototype.size = function(w, h) {
this.pin("width", w);
this.pin("height", h);
return this;
};
Class.prototype.width = function(w) {
if (typeof w === "undefined") {
return this.pin("width");
}
this.pin("width", w);
return this;
};
Class.prototype.height = function(h) {
if (typeof h === "undefined") {
return this.pin("height");
}
this.pin("height", h);
return this;
};
Class.prototype.offset = function(a, b) {
if (typeof a === "object") b = a.y, a = a.x;
this.pin("offsetX", a);
this.pin("offsetY", b);
return this;
};
Class.prototype.rotate = function(a) {
this.pin("rotation", a);
return this;
};
Class.prototype.skew = function(a, b) {
if (typeof a === "object") b = a.y, a = a.x; else if (typeof b === "undefined") b = a;
this.pin("skewX", a);
this.pin("skewY", b);
return this;
};
Class.prototype.scale = function(a, b) {
if (typeof a === "object") b = a.y, a = a.x; else if (typeof b === "undefined") b = a;
this.pin("scaleX", a);
this.pin("scaleY", b);
return this;
};
Class.prototype.alpha = function(a, ta) {
this.pin("alpha", a);
if (typeof ta !== "undefined") {
this.pin("textureAlpha", ta);
}
return this;
};
};
Pin._add_shortcuts(Class);
module.exports = Pin;
},{"./core":8,"./matrix":14}],17:[function(require,module,exports){
var Class = require("./core");
require("./pin");
var stats = require("./util/stats");
Class.prototype._textures = null;
Class.prototype._alpha = 1;
Class.prototype.render = function(context) {
if (!this._visible) {
return;
}
stats.node++;
var m = this.matrix();
context.setTransform(m.a, m.b, m.c, m.d, m.e, m.f);
// move this elsewhere!
this._alpha = this._pin._alpha * (this._parent ? this._parent._alpha : 1);
var alpha = this._pin._textureAlpha * this._alpha;
if (context.globalAlpha != alpha) {
context.globalAlpha = alpha;
}
if (this._textures !== null) {
for (var i = 0, n = this._textures.length; i < n; i++) {
this._textures[i].draw(context);
}
}
if (context.globalAlpha != this._alpha) {
context.globalAlpha = this._alpha;
}
var child, next = this._first;
while (child = next) {
next = child._next;
child.render(context);
}
};
Class.prototype._tickBefore = null;
Class.prototype._tickAfter = null;
Class.prototype.MAX_ELAPSE = Infinity;
Class.prototype._tick = function(elapsed, now, last) {
if (!this._visible) {
return;
}
if (elapsed > this.MAX_ELAPSE) {
elapsed = this.MAX_ELAPSE;
}
var ticked = false;
if (this._tickBefore !== null) {
for (var i = 0, n = this._tickBefore.length; i < n; i++) {
stats.tick++;
ticked = this._tickBefore[i].call(this, elapsed, now, last) === true || ticked;
}
}
var child, next = this._first;
while (child = next) {
next = child._next;
if (child._flag("_tick")) {
ticked = child._tick(elapsed, now, last) === true ? true : ticked;
}
}
if (this._tickAfter !== null) {
for (var i = 0, n = this._tickAfter.length; i < n; i++) {
stats.tick++;
ticked = this._tickAfter[i].call(this, elapsed, now, last) === true || ticked;
}
}
return ticked;
};
Class.prototype.tick = function(ticker, before) {
if (typeof ticker !== "function") {
return;
}
if (before) {
if (this._tickBefore === null) {
this._tickBefore = [];
}
this._tickBefore.push(ticker);
} else {
if (this._tickAfter === null) {
this._tickAfter = [];
}
this._tickAfter.push(ticker);
}
this._flag("_tick", this._tickAfter !== null && this._tickAfter.length > 0 || this._tickBefore !== null && this._tickBefore.length > 0);
};
Class.prototype.untick = function(ticker) {
if (typeof ticker !== "function") {
return;
}
var i;
if (this._tickBefore !== null && (i = this._tickBefore.indexOf(ticker)) >= 0) {
this._tickBefore.splice(i, 1);
}
if (this._tickAfter !== null && (i = this._tickAfter.indexOf(ticker)) >= 0) {
this._tickAfter.splice(i, 1);
}
};
Class.prototype.timeout = function(fn, time) {
this.tick(function timer(t) {
if ((time -= t) < 0) {
this.untick(timer);
fn.call(this);
} else {
return true;
}
});
};
},{"./core":8,"./pin":16,"./util/stats":29}],18:[function(require,module,exports){
var Class = require("./core");
require("./pin");
require("./render");
var stats = require("./util/stats");
var create = require("./util/create");
var extend = require("./util/extend");
Root._super = Class;
Root.prototype = create(Root._super.prototype);
Class.root = function(request, render) {
return new Root(request, render);
};
function Root(request, render) {
Root._super.call(this);
this.label("Root");
var paused = true;
var self = this;
var lastTime = 0;
var loop = function(now) {
if (paused === true) {
return;
}
stats.tick = stats.node = stats.draw = 0;
var last = lastTime || now;
var elapsed = now - last;
lastTime = now;
var ticked = self._tick(elapsed, now, last);
if (self._mo_touch != self._ts_touch) {
self._mo_touch = self._ts_touch;
render(self);
request(loop);
} else if (ticked) {
request(loop);
} else {
paused = true;
}
stats.fps = elapsed ? 1e3 / elapsed : 0;
};
this.start = function() {
return this.resume();
};
this.resume = function() {
if (paused) {
paused = false;
request(loop);
}
return this;
};
this.pause = function() {
paused = true;
return this;
};
this.touch_root = this.touch;
this.touch = function() {
this.resume();
return this.touch_root();
};
}
Root.prototype.background = function(color) {
// to be implemented by loaders
return this;
};
Root.prototype.viewport = function(width, height, ratio) {
if (typeof width === "undefined") {
return extend({}, this._viewport);
}
this._viewport = {
width: width,
height: height,
ratio: ratio || 1
};
this.viewbox();
var data = extend({}, this._viewport);
this.visit({
start: function(node) {
if (!node._flag("viewport")) {
return true;
}
node.publish("viewport", [ data ]);
}
});
return this;
};
// TODO: static/fixed viewbox
Root.prototype.viewbox = function(width, height, mode) {
if (typeof width === "number" && typeof height === "number") {
this._viewbox = {
width: width,
height: height,
mode: /^(in|out|in-pad|out-crop)$/.test(mode) ? mode : "in-pad"
};
}
var box = this._viewbox;
var size = this._viewport;
if (size && box) {
this.pin({
width: box.width,
height: box.height
});
this._pin._scaleTo(size.width, size.height, box.mode);
} else if (size) {
this.pin({
width: size.width,
height: size.height
});
}
return this;
};
},{"./core":8,"./pin":16,"./render":17,"./util/create":22,"./util/extend":24,"./util/stats":29}],19:[function(require,module,exports){
var Class = require("./core");
require("./pin");
require("./render");
var create = require("./util/create");
var is = require("./util/is");
Class.string = function(frames) {
return new Str().frames(frames);
};
Str._super = Class;
Str.prototype = create(Str._super.prototype);
function Str() {
Str._super.call(this);
this.label("String");
this._textures = [];
}
/**
* @deprecated Use frames
*/
Str.prototype.setFont = function(a, b, c) {
return this.frames(a, b, c);
};
Str.prototype.frames = function(frames) {
this._textures = [];
if (typeof frames == "string") {
frames = Class.texture(frames);
this._item = function(value) {
return frames.one(value);
};
} else if (typeof frames === "object") {
this._item = function(value) {
return frames[value];
};
} else if (typeof frames === "function") {
this._item = frames;
}
return this;
};
/**
* @deprecated Use value
*/
Str.prototype.setValue = function(a, b, c) {
return this.value(a, b, c);
};
Str.prototype.value = function(value) {
if (typeof value === "undefined") {
return this._value;
}
if (this._value === value) {
return this;
}
this._value = value;
if (value === null) {
value = "";
} else if (typeof value !== "string" && !is.array(value)) {
value = value.toString();
}
this._spacing = this._spacing || 0;
var width = 0, height = 0;
for (var i = 0; i < value.length; i++) {
var image = this._textures[i] = this._item(value[i]);
width += i > 0 ? this._spacing : 0;
image.dest(width, 0);
width = width + image.width;
height = Math.max(height, image.height);
}
this.pin("width", width);
this.pin("height", height);
this._textures.length = value.length;
return this;
};
},{"./core":8,"./pin":16,"./render":17,"./util/create":22,"./util/is":25}],20:[function(require,module,exports){
var stats = require("./util/stats");
var math = require("./util/math");
function Texture(image, ratio) {
if (typeof image === "object") {
this.src(image, ratio);
}
}
Texture.prototype.pipe = function() {
return new Texture(this);
};
/**
* Signatures: (image), (x, y, w, h), (w, h)
*/
Texture.prototype.src = function(x, y, w, h) {
if (typeof x === "object") {
var image = x, ratio = y || 1;
this._image = image;
this._sx = this._dx = 0;
this._sy = this._dy = 0;
this._sw = this._dw = image.width / ratio;
this._sh = this._dh = image.height / ratio;
this.width = image.width / ratio;
this.height = image.height / ratio;
this.ratio = ratio;
} else {
if (typeof w === "undefined") {
w = x, h = y;
} else {
this._sx = x, this._sy = y;
}
this._sw = this._dw = w;
this._sh = this._dh = h;
this.width = w;
this.height = h;
}
return this;
};
/**
* Signatures: (x, y, w, h), (x, y)
*/
Texture.prototype.dest = function(x, y, w, h) {
this._dx = x, this._dy = y;
this._dx = x, this._dy = y;
if (typeof w !== "undefined") {
this._dw = w, this._dh = h;
this.width = w, this.height = h;
}
return this;
};
Texture.prototype.draw = function(context, x1, y1, x2, y2, x3, y3, x4, y4) {
var image = this._image;
if (image === null || typeof image !== "object") {
return;
}
var sx = this._sx, sy = this._sy;
var sw = this._sw, sh = this._sh;
var dx = this._dx, dy = this._dy;
var dw = this._dw, dh = this._dh;
if (typeof x3 !== "undefined") {
x1 = math.limit(x1, 0, this._sw), x2 = math.limit(x2, 0, this._sw - x1);
y1 = math.limit(y1, 0, this._sh), y2 = math.limit(y2, 0, this._sh - y1);
sx += x1, sy += y1, sw = x2, sh = y2;
dx = x3, dy = y3, dw = x4, dh = y4;
} else if (typeof x2 !== "undefined") {
dx = x1, dy = y1, dw = x2, dh = y2;
} else if (typeof x1 !== "undefined") {
dw = x1, dh = y1;
}
var ratio = this.ratio || 1;
sx *= ratio, sy *= ratio, sw *= ratio, sh *= ratio;
try {
if (typeof image.draw === "function") {
image.draw(context, sx, sy, sw, sh, dx, dy, dw, dh);
} else {
stats.draw++;
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
}
} catch (ex) {
if (!image._draw_failed) {
console.log("Unable to draw: ", image);
console.log(ex);
image._draw_failed = true;
}
}
};
module.exports = Texture;
},{"./util/math":26,"./util/stats":29}],21:[function(require,module,exports){
module.exports = function() {
var await = 0;
function fork(fn, n) {
await += n = typeof n === "number" && n >= 1 ? n : 1;
return function() {
fn && fn.apply(this, arguments);
if (n > 0) {
n--, await--, call();
}
};
}
var then = [];
function call() {
if (await === 0) {
while (then.length) {
setTimeout(then.shift(), 0);
}
}
}
fork.then = function(fn) {
if (await === 0) {
setTimeout(fn, 0);
} else {
then.push(fn);
}
};
return fork;
};
},{}],22:[function(require,module,exports){
if (typeof Object.create == "function") {
module.exports = function(proto, props) {
return Object.create.call(Object, proto, props);
};
} else {
module.exports = function(proto, props) {
if (props) throw Error("Second argument is not supported!");
if (typeof proto !== "object" || proto === null) throw Error("Invalid prototype!");
noop.prototype = proto;
return new noop();
};
function noop() {}
}
},{}],23:[function(require,module,exports){
module.exports = function(prototype, callback) {
prototype._listeners = null;
prototype.on = prototype.listen = function(types, listener) {
if (!types || !types.length || typeof listener !== "function") {
return this;
}
if (this._listeners === null) {
this._listeners = {};
}
var isarray = typeof types !== "string" && typeof types.join === "function";
if (types = (isarray ? types.join(" ") : types).match(/\S+/g)) {
for (var i = 0; i < types.length; i++) {
var type = types[i];
this._listeners[type] = this._listeners[type] || [];
this._listeners[type].push(listener);
if (typeof callback === "function") {
callback(this, type, true);
}
}
}
return this;
};
prototype.off = function(types, listener) {
if (!types || !types.length || typeof listener !== "function") {
return this;
}
if (this._listeners === null) {
return this;
}
var isarray = typeof types !== "string" && typeof types.join === "function";
if (types = (isarray ? types.join(" ") : types).match(/\S+/g)) {
for (var i = 0; i < types.length; i++) {
var type = types[i], all = this._listeners[type], index;
if (all && (index = all.indexOf(listener)) >= 0) {
all.splice(index, 1);
if (!all.length) {
delete this._listeners[type];
}
if (typeof callback === "function") {
callback(this, type, false);
}
}
}
}
return this;
};
prototype.listeners = function(type) {
return this._listeners && this._listeners[type];
};
prototype.publish = function(name, args) {
var listeners = this.listeners(name);
if (!listeners || !listeners.length) {
return 0;
}
for (var l = 0; l < listeners.length; l++) {
listeners[l].apply(this, args);
}
return listeners.length;
};
prototype.trigger = function(name, args) {
this.publish(name, args);
return this;
};
};
},{}],24:[function(require,module,exports){
module.exports = function(base) {
for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
base[key] = obj[key];
}
}
}
return base;
};
},{}],25:[function(require,module,exports){
var objProto = Object.prototype;
var owns = objProto.hasOwnProperty;
var toStr = objProto.toString;
var NON_HOST_TYPES = {
"boolean": 1,
number: 1,
string: 1,
undefined: 1
};
var hexRegex = /^[A-Fa-f0-9]+$/;
var is = module.exports = {};
is.a = is.an = is.type = function(value, type) {
return typeof value === type;
};
is.defined = function(value) {
return typeof value !== "undefined";
};
is.empty = function(value) {
var type = toStr.call(value);
var key;
if ("[object Array]" === type || "[object Arguments]" === type || "[object String]" === type) {
return value.length === 0;
}
if ("[object Object]" === type) {
for (key in value) {
if (owns.call(value, key)) {
return false;
}
}
return true;
}
return !value;
};
is.equal = function(value, other) {
if (value === other) {
return true;
}
var type = toStr.call(value);
var key;
if (type !== toStr.call(other)) {
return false;
}
if ("[object Object]" === type) {
for (key in value) {
if (!is.equal(value[key], other[key]) || !(key in other)) {
return false;
}
}
for (key in other) {
if (!is.equal(value[key], other[key]) || !(key in value)) {
return false;
}
}
return true;
}
if ("[object Array]" === type) {
key = value.length;
if (key !== other.length) {
return false;
}
while (--key) {
if (!is.equal(value[key], other[key])) {
return false;
}
}
return true;
}
if ("[object Function]" === type) {
return value.prototype === other.prototype;
}
if ("[object Date]" === type) {
return value.getTime() === other.getTime();
}
return false;
};
is.instance = function(value, constructor) {
return value instanceof constructor;
};
is.nil = function(value) {
return value === null;
};
is.undef = function(value) {
return typeof value === "undefined";
};
is.array = function(value) {
return "[object Array]" === toStr.call(value);
};
is.emptyarray = function(value) {
return is.array(value) && value.length === 0;
};
is.arraylike = function(value) {
return !!value && !is.boolean(value) && owns.call(value, "length") && isFinite(value.length) && is.number(value.length) && value.length >= 0;
};
is.boolean = function(value) {
return "[object Boolean]" === toStr.call(value);
};
is.element = function(value) {
return value !== undefined && typeof HTMLElement !== "undefined" && value instanceof HTMLElement && value.nodeType === 1;
};
is.fn = function(value) {
return "[object Function]" === toStr.call(value);
};
is.number = function(value) {
return "[object Number]" === toStr.call(value);
};
is.nan = function(value) {
return !is.number(value) || value !== value;
};
is.object = function(value) {
return "[object Object]" === toStr.call(value);
};
is.hash = function(value) {
return is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
};
is.regexp = function(value) {
return "[object RegExp]" === toStr.call(value);
};
is.string = function(value) {
return "[object String]" === toStr.call(value);
};
is.hex = function(value) {
return is.string(value) && (!value.length || hexRegex.test(value));
};
},{}],26:[function(require,module,exports){
module.exports.random = function(min, max) {
if (typeof min === "undefined") {
max = 1, min = 0;
} else if (typeof max === "undefined") {
max = min, min = 0;
}
return min == max ? min : Math.random() * (max - min) + min;
};
module.exports.rotate = function(num, min, max) {
if (typeof min === "undefined") {
max = 1, min = 0;
} else if (typeof max === "undefined") {
max = min, min = 0;
}
if (max > min) {
num = (num - min) % (max - min);
return num + (num < 0 ? max : min);
} else {
num = (num - max) % (min - max);
return num + (num <= 0 ? min : max);
}
};
module.exports.limit = function(num, min, max) {
if (num < min) {
return min;
} else if (num > max) {
return max;
} else {
return num;
}
};
module.exports.length = function(x, y) {
return Math.sqrt(x * x + y * y);
};
},{}],27:[function(require,module,exports){
module.exports = function(fn, ctx) {
var called = false;
return function() {
if (!called) {
called = true;
fn.apply(ctx, arguments);
}
};
};
},{}],28:[function(require,module,exports){
module.exports = function(img, owidth, oheight, stretch, inner, insert) {
var width = img.width;
var height = img.height;
var left = img.left;
var right = img.right;
var top = img.top;
var bottom = img.bottom;
left = typeof left === "number" && left === left ? left : 0;
right = typeof right === "number" && right === right ? right : 0;
top = typeof top === "number" && top === top ? top : 0;
bottom = typeof bottom === "number" && bottom === bottom ? bottom : 0;
width = width - left - right;
height = height - top - bottom;
if (!inner) {
owidth = Math.max(owidth - left - right, 0);
oheight = Math.max(oheight - top - bottom, 0);
}
var i = 0;
if (top > 0 && left > 0) insert(i++, 0, 0, left, top, 0, 0, left, top);
if (bottom > 0 && left > 0) insert(i++, 0, height + top, left, bottom, 0, oheight + top, left, bottom);
if (top > 0 && right > 0) insert(i++, width + left, 0, right, top, owidth + left, 0, right, top);
if (bottom > 0 && right > 0) insert(i++, width + left, height + top, right, bottom, owidth + left, oheight + top, right, bottom);
if (stretch) {
if (top > 0) insert(i++, left, 0, width, top, left, 0, owidth, top);
if (bottom > 0) insert(i++, left, height + top, width, bottom, left, oheight + top, owidth, bottom);
if (left > 0) insert(i++, 0, top, left, height, 0, top, left, oheight);
if (right > 0) insert(i++, width + left, top, right, height, owidth + left, top, right, oheight);
// center
insert(i++, left, top, width, height, left, top, owidth, oheight);
} else {
// tile
var l = left, r = owidth, w;
while (r > 0) {
w = Math.min(width, r), r -= width;
var t = top, b = oheight, h;
while (b > 0) {
h = Math.min(height, b), b -= height;
insert(i++, left, top, w, h, l, t, w, h);
if (r <= 0) {
if (left) insert(i++, 0, top, left, h, 0, t, left, h);
if (right) insert(i++, width + left, top, right, h, l + w, t, right, h);
}
t += h;
}
if (top) insert(i++, left, 0, w, top, l, 0, w, top);
if (bottom) insert(i++, left, height + top, w, bottom, l, t, w, bottom);
l += w;
}
}
return i;
};
},{}],29:[function(require,module,exports){
module.exports = {};
},{}],30:[function(require,module,exports){
module.exports.startsWith = function(str, sub) {
return typeof str === "string" && typeof sub === "string" && str.substring(0, sub.length) == sub;
};
},{}]},{},[1])(1)
});
function Game() {
var cells = [], turn = 0, ready = false;
this.ready = function() {
return ready;
};
this.init = function() {
console.log('Game.init');
cells = [];
for (var i = -1; i <= 1; i++) {
cells[i] = [];
for (var j = -1; j <= 1; j++) {
cells[i][j] = {
i : i,
j : j
};
this.uiNewCell(cells[i][j]);
}
}
};
this.start = function() {
console.log('Game.start');
if (cells.length == 0) {
this.init();
}
turn = 0, ready = true;
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
cells[i][j].sign = null;
this.uiUpdateCell(cells[i][j]);
}
}
};
this.click = function(cell) {
console.log('Game.click');
if (!ready || cell.sign) {
return;
}
cell.sign = (turn++ % 2 == 0) ? 'o' : 'x';
this.uiUpdateCell(cell);
var row = test(cell.i, cell.j, cell.sign);
if (row) {
ready = false;
this.uiWin(row, cell.sign);
} else if (turn >= 9) {
ready = false;
this.uiDraw(row, cell.sign);
}
};
function test(i, j, sign) {
console.log('Game.test');
if (cells[-1][j].sign == sign && cells[0][j].sign == sign
&& cells[+1][j].sign == sign)
return [ cells[-1][j], cells[0][j], cells[+1][j] ];
if (cells[i][-1].sign == sign && cells[i][0].sign == sign
&& cells[i][+1].sign == sign)
return [ cells[i][-1], cells[i][0], cells[i][+1] ];
if (i == j && cells[-1][-1].sign == sign && cells[0][0].sign == sign
&& cells[+1][+1].sign == sign)
return [ cells[-1][-1], cells[0][0], cells[+1][+1] ];
if (i == -j && cells[-1][+1].sign == sign && cells[0][0].sign == sign
&& cells[+1][-1].sign == sign)
return [ cells[-1][+1], cells[0][0], cells[+1][-1] ];
}
}
Stage(function(stage) {
stage.viewbox(50, 50).pin('handle', -0.5);
Stage.image('bg').pin('handle', 0.5).appendTo(stage);
var game = new Game();
game.uiNewCell = function(cell) {
console.log('Game.uiNewCell');
cell.ui = Stage.image('x').appendTo(stage).pin({
offsetX : cell.i * 10,
offsetY : cell.j * 10,
handle : 0.5
}).on('click', function() {
if (game.ready()) {
game.click(cell);
} else {
game.start();
}
});
};
game.uiUpdateCell = function(cell) {
console.log('Box.uiUpdateCell');
cell.ui.image(cell.sign || '-');
cell.ui.pin({
alpha : 0.8,
scale : 1
});
};
game.uiWin = function name(row, sign) {
console.log('Game.uiWin');
for (var i = 0; i < row.length; i++) {
var tween = row[i].ui.tween(200).pin({
alpha : 1,
scaleX : 1.2,
scaleY : 1.2
});
}
};
game.uiDraw = function() {
console.log('Game.uiDraw');
};
game.start();
});
Stage({
textures : {
'bg' : Stage.canvas(function(ctx) {
var ratio = 20;
this.size(30, 30, ratio);
ctx.scale(ratio, ratio);
ctx.moveTo(10, 1);
ctx.lineTo(10, 29);
ctx.moveTo(20, 1);
ctx.lineTo(20, 29);
ctx.moveTo(1, 10);
ctx.lineTo(29, 10);
ctx.moveTo(1, 20);
ctx.lineTo(29, 20);
ctx.lineWidth = 0.3;
ctx.lineCap = 'round';
ctx.strokeStyle = '#999';
ctx.stroke();
}),
'x' : Stage.canvas(function(ctx) {
var ratio = 20;
this.size(10, 10, ratio);
ctx.scale(ratio, ratio);
ctx.moveTo(2, 2);
ctx.lineTo(8, 8);
ctx.moveTo(2, 8);
ctx.lineTo(8, 2);
ctx.lineWidth = 0.5;
ctx.lineCap = 'round';
ctx.strokeStyle = '#000';
ctx.stroke();
}),
'o' : Stage.canvas(function(ctx) {
var ratio = 20;
this.size(10, 10, ratio);
ctx.scale(ratio, ratio);
ctx.arc(5, 5, 2.4, 0, 2 * Math.PI);
ctx.lineWidth = 0.5;
ctx.strokeStyle = '#000';
ctx.stroke();
}),
'-' : Stage.canvas(function(ctx) {
var ratio = 20;
this.size(10, 10, ratio);
})
}
});
This Pen doesn't use any external CSS resources.