Skip to content

Instantly share code, notes, and snippets.

@trusktr
Created October 5, 2017 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trusktr/e4d5a9bcf4c64b62abbebf2d8bb34ddd to your computer and use it in GitHub Desktop.
Save trusktr/e4d5a9bcf4c64b62abbebf2d8bb34ddd to your computer and use it in GitHub Desktop.
var infamous = (function (exports) {
'use strict';
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A high-performance static matrix math library used to calculate
* affine transforms on surfaces and other renderables.
* Famo.us uses 4x4 matrices corresponding directly to
* WebKit matrices (column-major order).
*
* The internal "type" of a Matrix is a 16-long float array in
* row-major order, with:
* elements [0],[1],[2],[4],[5],[6],[8],[9],[10] forming the 3x3
* transformation matrix;
* elements [12], [13], [14] corresponding to the t_x, t_y, t_z
* translation;
* elements [3], [7], [11] set to 0;
* element [15] set to 1.
* All methods are static.
*
* @static
*
* @class Transform
*/
var Transform = {};
// WARNING: these matrices correspond to WebKit matrices, which are
// transposed from their math counterparts
Transform.precision = 1e-6;
Transform.identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
/**
* Multiply two or more Transform matrix types to return a Transform matrix.
*
* @method multiply4x4
* @static
* @param {Transform} a left Transform
* @param {Transform} b right Transform
* @return {Transform}
*/
Transform.multiply4x4 = function multiply4x4(a, b) {
return [
a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
];
};
/**
* Fast-multiply two Transform matrix types to return a
* Matrix, assuming bottom row on each is [0 0 0 1].
*
* @method multiply
* @static
* @param {Transform} a left Transform
* @param {Transform} b right Transform
* @return {Transform}
*/
Transform.multiply = function multiply(a, b) {
return [
a[0] * b[0] + a[4] * b[1] + a[8] * b[2],
a[1] * b[0] + a[5] * b[1] + a[9] * b[2],
a[2] * b[0] + a[6] * b[1] + a[10] * b[2],
0,
a[0] * b[4] + a[4] * b[5] + a[8] * b[6],
a[1] * b[4] + a[5] * b[5] + a[9] * b[6],
a[2] * b[4] + a[6] * b[5] + a[10] * b[6],
0,
a[0] * b[8] + a[4] * b[9] + a[8] * b[10],
a[1] * b[8] + a[5] * b[9] + a[9] * b[10],
a[2] * b[8] + a[6] * b[9] + a[10] * b[10],
0,
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12],
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13],
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14],
1
];
};
/**
* Return a Transform translated by additional amounts in each
* dimension. This is equivalent to the result of
*
* Transform.multiply(Matrix.translate(t[0], t[1], t[2]), m).
*
* @method thenMove
* @static
* @param {Transform} m a Transform
* @param {Array.Number} t floats delta vector of length 2 or 3
* @return {Transform}
*/
Transform.thenMove = function thenMove(m, t) {
if (!t[2]) { t[2] = 0; }
return [m[0], m[1], m[2], 0, m[4], m[5], m[6], 0, m[8], m[9], m[10], 0, m[12] + t[0], m[13] + t[1], m[14] + t[2], 1];
};
/**
* Return a Transform matrix which represents the result of a transform matrix
* applied after a move. This is faster than the equivalent multiply.
* This is equivalent to the result of:
*
* Transform.multiply(m, Transform.translate(t[0], t[1], t[2])).
*
* @method moveThen
* @static
* @param {Array.Number} v vector representing initial movement
* @param {Transform} m matrix to apply afterwards
* @return {Transform} the resulting matrix
*/
Transform.moveThen = function moveThen(v, m) {
if (!v[2]) { v[2] = 0; }
var t0 = v[0] * m[0] + v[1] * m[4] + v[2] * m[8];
var t1 = v[0] * m[1] + v[1] * m[5] + v[2] * m[9];
var t2 = v[0] * m[2] + v[1] * m[6] + v[2] * m[10];
return Transform.thenMove(m, [t0, t1, t2]);
};
/**
* Return a Transform which represents a translation by specified
* amounts in each dimension.
*
* @method translate
* @static
* @param {Number} x x translation
* @param {Number} y y translation
* @param {Number} z z translation
* @return {Transform}
*/
Transform.translate = function translate(x, y, z) {
if (z === undefined) { z = 0; }
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1];
};
/**
* Return a Transform scaled by a vector in each
* dimension. This is a more performant equivalent to the result of
*
* Transform.multiply(Transform.scale(s[0], s[1], s[2]), m).
*
* @method thenScale
* @static
* @param {Transform} m a matrix
* @param {Array.Number} s delta vector (array of floats &&
* array.length == 3)
* @return {Transform}
*/
Transform.thenScale = function thenScale(m, s) {
return [
s[0] * m[0], s[1] * m[1], s[2] * m[2], 0,
s[0] * m[4], s[1] * m[5], s[2] * m[6], 0,
s[0] * m[8], s[1] * m[9], s[2] * m[10], 0,
s[0] * m[12], s[1] * m[13], s[2] * m[14], 1
];
};
/**
* Return a Transform which represents a scale by specified amounts
* in each dimension.
*
* @method scale
* @static
* @param {Number} x x scale factor
* @param {Number} y y scale factor
* @param {Number} z z scale factor
* @return {Transform}
*/
Transform.scale = function scale(x, y, z) {
if (z === undefined) { z = 1; }
if (y === undefined) { y = x; }
return [x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1];
};
/**
* Return a Transform which represents a clockwise
* rotation around the x axis.
*
* @method rotateX
* @static
* @param {Number} theta radians
* @return {Transform}
*/
Transform.rotateX = function rotateX(theta) {
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
return [1, 0, 0, 0, 0, cosTheta, sinTheta, 0, 0, -sinTheta, cosTheta, 0, 0, 0, 0, 1];
};
/**
* Return a Transform which represents a clockwise
* rotation around the y axis.
*
* @method rotateY
* @static
* @param {Number} theta radians
* @return {Transform}
*/
Transform.rotateY = function rotateY(theta) {
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
return [cosTheta, 0, -sinTheta, 0, 0, 1, 0, 0, sinTheta, 0, cosTheta, 0, 0, 0, 0, 1];
};
/**
* Return a Transform which represents a clockwise
* rotation around the z axis.
*
* @method rotateZ
* @static
* @param {Number} theta radians
* @return {Transform}
*/
Transform.rotateZ = function rotateZ(theta) {
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
return [cosTheta, sinTheta, 0, 0, -sinTheta, cosTheta, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
};
/**
* Return a Transform which represents composed clockwise
* rotations along each of the axes. Equivalent to the result of
* Matrix.multiply(rotateX(phi), rotateY(theta), rotateZ(psi)).
*
* @method rotate
* @static
* @param {Number} phi radians to rotate about the positive x axis
* @param {Number} theta radians to rotate about the positive y axis
* @param {Number} psi radians to rotate about the positive z axis
* @return {Transform}
*/
Transform.rotate = function rotate(phi, theta, psi) {
var cosPhi = Math.cos(phi);
var sinPhi = Math.sin(phi);
var cosTheta = Math.cos(theta);
var sinTheta = Math.sin(theta);
var cosPsi = Math.cos(psi);
var sinPsi = Math.sin(psi);
var result = [
cosTheta * cosPsi,
cosPhi * sinPsi + sinPhi * sinTheta * cosPsi,
sinPhi * sinPsi - cosPhi * sinTheta * cosPsi,
0,
-cosTheta * sinPsi,
cosPhi * cosPsi - sinPhi * sinTheta * sinPsi,
sinPhi * cosPsi + cosPhi * sinTheta * sinPsi,
0,
sinTheta,
-sinPhi * cosTheta,
cosPhi * cosTheta,
0,
0, 0, 0, 1
];
return result;
};
/**
* Return a Transform which represents an axis-angle rotation
*
* @method rotateAxis
* @static
* @param {Array.Number} v unit vector representing the axis to rotate about
* @param {Number} theta radians to rotate clockwise about the axis
* @return {Transform}
*/
Transform.rotateAxis = function rotateAxis(v, theta) {
var sinTheta = Math.sin(theta);
var cosTheta = Math.cos(theta);
var verTheta = 1 - cosTheta; // versine of theta
var xxV = v[0] * v[0] * verTheta;
var xyV = v[0] * v[1] * verTheta;
var xzV = v[0] * v[2] * verTheta;
var yyV = v[1] * v[1] * verTheta;
var yzV = v[1] * v[2] * verTheta;
var zzV = v[2] * v[2] * verTheta;
var xs = v[0] * sinTheta;
var ys = v[1] * sinTheta;
var zs = v[2] * sinTheta;
var result = [
xxV + cosTheta, xyV + zs, xzV - ys, 0,
xyV - zs, yyV + cosTheta, yzV + xs, 0,
xzV + ys, yzV - xs, zzV + cosTheta, 0,
0, 0, 0, 1
];
return result;
};
/**
* Return a Transform which represents a transform matrix applied about
* a separate origin point.
*
* @method aboutOrigin
* @static
* @param {Array.Number} v origin point to apply matrix
* @param {Transform} m matrix to apply
* @return {Transform}
*/
Transform.aboutOrigin = function aboutOrigin(v, m) {
var t0 = v[0] - (v[0] * m[0] + v[1] * m[4] + v[2] * m[8]);
var t1 = v[1] - (v[0] * m[1] + v[1] * m[5] + v[2] * m[9]);
var t2 = v[2] - (v[0] * m[2] + v[1] * m[6] + v[2] * m[10]);
return Transform.thenMove(m, [t0, t1, t2]);
};
/**
* Return a Transform representation of a skew transformation
*
* @method skew
* @static
* @param {Number} phi scale factor skew in the x axis
* @param {Number} theta scale factor skew in the y axis
* @param {Number} psi scale factor skew in the z axis
* @return {Transform}
*/
Transform.skew = function skew(phi, theta, psi) {
return [1, Math.tan(theta), 0, 0, Math.tan(psi), 1, 0, 0, 0, Math.tan(phi), 1, 0, 0, 0, 0, 1];
};
/**
* Return a Transform representation of a skew in the x-direction
*
* @method skewX
* @static
* @param {Number} angle the angle between the top and left sides
* @return {Transform}
*/
Transform.skewX = function skewX(angle) {
return [1, 0, 0, 0, Math.tan(angle), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
};
/**
* Return a Transform representation of a skew in the y-direction
*
* @method skewY
* @static
* @param {Number} angle the angle between the top and right sides
* @return {Transform}
*/
Transform.skewY = function skewY(angle) {
return [1, Math.tan(angle), 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
};
/**
* Returns a perspective Transform matrix
*
* @method perspective
* @static
* @param {Number} focusZ z position of focal point
* @return {Transform}
*/
Transform.perspective = function perspective(focusZ) {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1 / focusZ, 0, 0, 0, 1];
};
/**
* Return translation vector component of given Transform
*
* @method getTranslate
* @static
* @param {Transform} m Transform
* @return {Array.Number} the translation vector [t_x, t_y, t_z]
*/
Transform.getTranslate = function getTranslate(m) {
return [m[12], m[13], m[14]];
};
/**
* Return inverse affine transform for given Transform.
* Note: This assumes m[3] = m[7] = m[11] = 0, and m[15] = 1.
* Will provide incorrect results if not invertible or preconditions not met.
*
* @method inverse
* @static
* @param {Transform} m Transform
* @return {Transform}
*/
Transform.inverse = function inverse(m) {
// only need to consider 3x3 section for affine
var c0 = m[5] * m[10] - m[6] * m[9];
var c1 = m[4] * m[10] - m[6] * m[8];
var c2 = m[4] * m[9] - m[5] * m[8];
var c4 = m[1] * m[10] - m[2] * m[9];
var c5 = m[0] * m[10] - m[2] * m[8];
var c6 = m[0] * m[9] - m[1] * m[8];
var c8 = m[1] * m[6] - m[2] * m[5];
var c9 = m[0] * m[6] - m[2] * m[4];
var c10 = m[0] * m[5] - m[1] * m[4];
var detM = m[0] * c0 - m[1] * c1 + m[2] * c2;
var invD = 1 / detM;
var result = [
invD * c0, -invD * c4, invD * c8, 0,
-invD * c1, invD * c5, -invD * c9, 0,
invD * c2, -invD * c6, invD * c10, 0,
0, 0, 0, 1
];
result[12] = -m[12] * result[0] - m[13] * result[4] - m[14] * result[8];
result[13] = -m[12] * result[1] - m[13] * result[5] - m[14] * result[9];
result[14] = -m[12] * result[2] - m[13] * result[6] - m[14] * result[10];
return result;
};
/**
* Returns the transpose of a 4x4 matrix
*
* @method transpose
* @static
* @param {Transform} m matrix
* @return {Transform} the resulting transposed matrix
*/
Transform.transpose = function transpose(m) {
return [m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]];
};
function _normSquared(v) {
return (v.length === 2) ? v[0] * v[0] + v[1] * v[1] : v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
function _norm(v) {
return Math.sqrt(_normSquared(v));
}
function _sign(n) {
return (n < 0) ? -1 : 1;
}
/**
* Decompose Transform into separate .translate, .rotate, .scale,
* and .skew components.
*
* @method interpret
* @static
* @param {Transform} M transform matrix
* @return {Object} matrix spec object with component matrices .translate,
* .rotate, .scale, .skew
*/
Transform.interpret = function interpret(M) {
// QR decomposition via Householder reflections
//FIRST ITERATION
//default Q1 to the identity matrix;
var x = [M[0], M[1], M[2]]; // first column vector
var sgn = _sign(x[0]); // sign of first component of x (for stability)
var xNorm = _norm(x); // norm of first column vector
var v = [x[0] + sgn * xNorm, x[1], x[2]]; // v = x + sign(x[0])|x|e1
var mult = 2 / _normSquared(v); // mult = 2/v'v
//bail out if our Matrix is singular
if (mult >= Infinity) {
return {translate: Transform.getTranslate(M), rotate: [0, 0, 0], scale: [0, 0, 0], skew: [0, 0, 0]};
}
//evaluate Q1 = I - 2vv'/v'v
var Q1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
//diagonals
Q1[0] = 1 - mult * v[0] * v[0]; // 0,0 entry
Q1[5] = 1 - mult * v[1] * v[1]; // 1,1 entry
Q1[10] = 1 - mult * v[2] * v[2]; // 2,2 entry
//upper diagonal
Q1[1] = -mult * v[0] * v[1]; // 0,1 entry
Q1[2] = -mult * v[0] * v[2]; // 0,2 entry
Q1[6] = -mult * v[1] * v[2]; // 1,2 entry
//lower diagonal
Q1[4] = Q1[1]; // 1,0 entry
Q1[8] = Q1[2]; // 2,0 entry
Q1[9] = Q1[6]; // 2,1 entry
//reduce first column of M
var MQ1 = Transform.multiply(Q1, M);
//SECOND ITERATION on (1,1) minor
var x2 = [MQ1[5], MQ1[6]];
var sgn2 = _sign(x2[0]); // sign of first component of x (for stability)
var x2Norm = _norm(x2); // norm of first column vector
var v2 = [x2[0] + sgn2 * x2Norm, x2[1]]; // v = x + sign(x[0])|x|e1
var mult2 = 2 / _normSquared(v2); // mult = 2/v'v
//evaluate Q2 = I - 2vv'/v'v
var Q2 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
//diagonal
Q2[5] = 1 - mult2 * v2[0] * v2[0]; // 1,1 entry
Q2[10] = 1 - mult2 * v2[1] * v2[1]; // 2,2 entry
//off diagonals
Q2[6] = -mult2 * v2[0] * v2[1]; // 2,1 entry
Q2[9] = Q2[6]; // 1,2 entry
//calc QR decomposition. Q = Q1*Q2, R = Q'*M
var Q = Transform.multiply(Q2, Q1); //note: really Q transpose
var R = Transform.multiply(Q, M);
//remove negative scaling
var remover = Transform.scale(R[0] < 0 ? -1 : 1, R[5] < 0 ? -1 : 1, R[10] < 0 ? -1 : 1);
R = Transform.multiply(R, remover);
Q = Transform.multiply(remover, Q);
//decompose into rotate/scale/skew matrices
var result = {};
result.translate = Transform.getTranslate(M);
result.rotate = [Math.atan2(-Q[6], Q[10]), Math.asin(Q[2]), Math.atan2(-Q[1], Q[0])];
if (!result.rotate[0]) {
result.rotate[0] = 0;
result.rotate[2] = Math.atan2(Q[4], Q[5]);
}
result.scale = [R[0], R[5], R[10]];
result.skew = [Math.atan2(R[9], result.scale[2]), Math.atan2(R[8], result.scale[2]), Math.atan2(R[4], result.scale[0])];
//double rotation workaround
if (Math.abs(result.rotate[0]) + Math.abs(result.rotate[2]) > 1.5 * Math.PI) {
result.rotate[1] = Math.PI - result.rotate[1];
if (result.rotate[1] > Math.PI) { result.rotate[1] -= 2 * Math.PI; }
if (result.rotate[1] < -Math.PI) { result.rotate[1] += 2 * Math.PI; }
if (result.rotate[0] < 0) { result.rotate[0] += Math.PI; }
else { result.rotate[0] -= Math.PI; }
if (result.rotate[2] < 0) { result.rotate[2] += Math.PI; }
else { result.rotate[2] -= Math.PI; }
}
return result;
};
/**
* Weighted average between two matrices by averaging their
* translation, rotation, scale, skew components.
* f(M1,M2,t) = (1 - t) * M1 + t * M2
*
* @method average
* @static
* @param {Transform} M1 f(M1,M2,0) = M1
* @param {Transform} M2 f(M1,M2,1) = M2
* @param {Number} t
* @return {Transform}
*/
Transform.average = function average(M1, M2, t) {
t = (t === undefined) ? 0.5 : t;
var specM1 = Transform.interpret(M1);
var specM2 = Transform.interpret(M2);
var specAvg = {
translate: [0, 0, 0],
rotate: [0, 0, 0],
scale: [0, 0, 0],
skew: [0, 0, 0]
};
for (var i = 0; i < 3; i++) {
specAvg.translate[i] = (1 - t) * specM1.translate[i] + t * specM2.translate[i];
specAvg.rotate[i] = (1 - t) * specM1.rotate[i] + t * specM2.rotate[i];
specAvg.scale[i] = (1 - t) * specM1.scale[i] + t * specM2.scale[i];
specAvg.skew[i] = (1 - t) * specM1.skew[i] + t * specM2.skew[i];
}
return Transform.build(specAvg);
};
/**
* Compose .translate, .rotate, .scale, .skew components into
* Transform matrix
*
* @method build
* @static
* @param {matrixSpec} spec object with component matrices .translate,
* .rotate, .scale, .skew
* @return {Transform} composed transform
*/
Transform.build = function build(spec) {
var scaleMatrix = Transform.scale(spec.scale[0], spec.scale[1], spec.scale[2]);
var skewMatrix = Transform.skew(spec.skew[0], spec.skew[1], spec.skew[2]);
var rotateMatrix = Transform.rotate(spec.rotate[0], spec.rotate[1], spec.rotate[2]);
return Transform.thenMove(Transform.multiply(Transform.multiply(rotateMatrix, skewMatrix), scaleMatrix), spec.translate);
};
/**
* Determine if two Transforms are component-wise equal
* Warning: breaks on perspective Transforms
*
* @method equals
* @static
* @param {Transform} a matrix
* @param {Transform} b matrix
* @return {boolean}
*/
Transform.equals = function equals(a, b) {
return !Transform.notEquals(a, b);
};
/**
* Determine if two Transforms are component-wise unequal
* Warning: breaks on perspective Transforms
*
* @method notEquals
* @static
* @param {Transform} a matrix
* @param {Transform} b matrix
* @return {boolean}
*/
Transform.notEquals = function notEquals(a, b) {
if (a === b) { return false; }
// shortci
return !(a && b) ||
a[12] !== b[12] || a[13] !== b[13] || a[14] !== b[14] ||
a[0] !== b[0] || a[1] !== b[1] || a[2] !== b[2] ||
a[4] !== b[4] || a[5] !== b[5] || a[6] !== b[6] ||
a[8] !== b[8] || a[9] !== b[9] || a[10] !== b[10];
};
/**
* Constrain angle-trio components to range of [-pi, pi).
*
* @method normalizeRotation
* @static
* @param {Array.Number} rotation phi, theta, psi (array of floats
* && array.length == 3)
* @return {Array.Number} new phi, theta, psi triplet
* (array of floats && array.length == 3)
*/
Transform.normalizeRotation = function normalizeRotation(rotation) {
var result = rotation.slice(0);
if (result[0] === Math.PI * 0.5 || result[0] === -Math.PI * 0.5) {
result[0] = -result[0];
result[1] = Math.PI - result[1];
result[2] -= Math.PI;
}
if (result[0] > Math.PI * 0.5) {
result[0] = result[0] - Math.PI;
result[1] = Math.PI - result[1];
result[2] -= Math.PI;
}
if (result[0] < -Math.PI * 0.5) {
result[0] = result[0] + Math.PI;
result[1] = -Math.PI - result[1];
result[2] -= Math.PI;
}
while (result[1] < -Math.PI) { result[1] += 2 * Math.PI; }
while (result[1] >= Math.PI) { result[1] -= 2 * Math.PI; }
while (result[2] < -Math.PI) { result[2] += 2 * Math.PI; }
while (result[2] >= Math.PI) { result[2] -= 2 * Math.PI; }
return result;
};
/**
* (Property) Array defining a translation forward in z by 1
*
* @property {array} inFront
* @static
* @final
*/
Transform.inFront = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1e-3, 1];
/**
* (Property) Array defining a translation backwards in z by 1
*
* @property {array} behind
* @static
* @final
*/
Transform.behind = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -1e-3, 1];
var Transform_1 = Transform;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* This namespace holds standalone functionality.
* Currently includes name mapping for transition curves,
* name mapping for origin pairs, and the after() function.
*
* @class Utility
* @static
*/
var Utility = {};
/**
* Table of direction array positions
*
* @property {object} Direction
* @final
*/
Utility.Direction = {
X: 0,
Y: 1,
Z: 2
};
/**
* Return wrapper around callback function. Once the wrapper is called N
* times, invoke the callback function. Arguments and scope preserved.
*
* @method after
*
* @param {number} count number of calls before callback function invoked
* @param {Function} callback wrapped callback function
*
* @return {function} wrapped callback with coundown feature
*/
Utility.after = function after(count, callback) {
var counter = count;
return function() {
counter--;
if (counter === 0) { callback.apply(this, arguments); }
};
};
/**
* Load a URL and return its contents in a callback
*
* @method loadURL
*
* @param {string} url URL of object
* @param {function} callback callback to dispatch with content
*/
Utility.loadURL = function loadURL(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function onreadystatechange() {
if (this.readyState === 4) {
if (callback) { callback(this.responseText); }
}
};
xhr.open('GET', url);
xhr.send();
};
/**
* Create a document fragment from a string of HTML
*
* @method createDocumentFragmentFromHTML
*
* @param {string} html HTML to convert to DocumentFragment
*
* @return {DocumentFragment} DocumentFragment representing input HTML
*/
Utility.createDocumentFragmentFromHTML = function createDocumentFragmentFromHTML(html) {
var element = document.createElement('div');
element.innerHTML = html;
var result = document.createDocumentFragment();
while (element.hasChildNodes()) { result.appendChild(element.firstChild); }
return result;
};
/*
* Deep clone an object.
* @param b {Object} Object to clone
* @return a {Object} Cloned object.
*/
Utility.clone = function clone(b) {
var a;
if (typeof b === 'object') {
a = (b instanceof Array) ? [] : {};
for (var key in b) {
if (typeof b[key] === 'object' && b[key] !== null) {
if (b[key] instanceof Array) {
a[key] = new Array(b[key].length);
for (var i = 0; i < b[key].length; i++) {
a[key][i] = Utility.clone(b[key][i]);
}
}
else {
a[key] = Utility.clone(b[key]);
}
}
else {
a[key] = b[key];
}
}
}
else {
a = b;
}
return a;
};
var Utility_1 = Utility;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: david@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/*eslint-disable new-cap */
/**
* Transition meta-method to support transitioning multiple
* values with scalar-only methods.
*
*
* @class MultipleTransition
* @constructor
*
* @param {Object} method Transionable class to multiplex
*/
function MultipleTransition(method) {
this.method = method;
this._instances = [];
this.state = [];
}
MultipleTransition.SUPPORTS_MULTIPLE = true;
/**
* Get the state of each transition.
*
* @method get
*
* @return state {Number|Array} state array
*/
MultipleTransition.prototype.get = function get() {
for (var i = 0; i < this._instances.length; i++) {
this.state[i] = this._instances[i].get();
}
return this.state;
};
/**
* Set the end states with a shared transition, with optional callback.
*
* @method set
*
* @param {Number|Array} endState Final State. Use a multi-element argument for multiple transitions.
* @param {Object} transition Transition definition, shared among all instances
* @param {Function} callback called when all endStates have been reached.
*/
MultipleTransition.prototype.set = function set(endState, transition, callback) {
var _allCallback = Utility_1.after(endState.length, callback);
for (var i = 0; i < endState.length; i++) {
if (!this._instances[i]) { this._instances[i] = new (this.method)(); }
this._instances[i].set(endState[i], transition, _allCallback);
}
};
/**
* Reset all transitions to start state.
*
* @method reset
*
* @param {Number|Array} startState Start state
*/
MultipleTransition.prototype.reset = function reset(startState) {
for (var i = 0; i < startState.length; i++) {
if (!this._instances[i]) { this._instances[i] = new (this.method)(); }
this._instances[i].reset(startState[i]);
}
};
var MultipleTransition_1 = MultipleTransition;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: david@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
*
* A state maintainer for a smooth transition between
* numerically-specified states. Example numeric states include floats or
* Transfornm objects.
*
* An initial state is set with the constructor or set(startValue). A
* corresponding end state and transition are set with set(endValue,
* transition). Subsequent calls to set(endValue, transition) begin at
* the last state. Calls to get(timestamp) provide the _interpolated state
* along the way.
*
* Note that there is no event loop here - calls to get() are the only way
* to find out state projected to the current (or provided) time and are
* the only way to trigger callbacks. Usually this kind of object would
* be part of the render() path of a visible component.
*
* @class TweenTransition
* @constructor
*
* @param {Object} options TODO
* beginning state
*/
function TweenTransition(options) {
this.options = Object.create(TweenTransition.DEFAULT_OPTIONS);
if (options) { this.setOptions(options); }
this._startTime = 0;
this._startValue = 0;
this._updateTime = 0;
this._endValue = 0;
this._curve = undefined;
this._duration = 0;
this._active = false;
this._callback = undefined;
this.state = 0;
this.velocity = undefined;
}
/**
* Transition curves mapping independent variable t from domain [0,1] to a
* range within [0,1]. Includes functions 'linear', 'easeIn', 'easeOut',
* 'easeInOut', 'easeOutBounce', 'spring'.
*
* @property {object} Curve
* @final
*/
TweenTransition.Curves = {
linear: function(t) {
return t;
},
easeIn: function(t) {
return t*t;
},
easeOut: function(t) {
return t*(2-t);
},
easeInOut: function(t) {
if (t <= 0.5) { return 2*t*t; }
else { return -2*t*t + 4*t - 1; }
},
easeOutBounce: function(t) {
return t*(3 - 2*t);
},
spring: function(t) {
return (1 - t) * Math.sin(6 * Math.PI * t) + t;
}
};
TweenTransition.SUPPORTS_MULTIPLE = true;
TweenTransition.DEFAULT_OPTIONS = {
curve: TweenTransition.Curves.linear,
duration: 500,
speed: 0 /* considered only if positive */
};
var registeredCurves = {};
/**
* Add "unit" curve to internal dictionary of registered curves.
*
* @method registerCurve
*
* @static
*
* @param {string} curveName dictionary key
* @param {unitCurve} curve function of one numeric variable mapping [0,1]
* to range inside [0,1]
* @return {boolean} false if key is taken, else true
*/
TweenTransition.registerCurve = function registerCurve(curveName, curve) {
if (!registeredCurves[curveName]) {
registeredCurves[curveName] = curve;
return true;
}
else {
return false;
}
};
/**
* Remove object with key "curveName" from internal dictionary of registered
* curves.
*
* @method unregisterCurve
*
* @static
*
* @param {string} curveName dictionary key
* @return {boolean} false if key has no dictionary value
*/
TweenTransition.unregisterCurve = function unregisterCurve(curveName) {
if (registeredCurves[curveName]) {
delete registeredCurves[curveName];
return true;
}
else {
return false;
}
};
/**
* Retrieve function with key "curveName" from internal dictionary of
* registered curves. Default curves are defined in the
* TweenTransition.Curves array, where the values represent
* unitCurve functions.
*
* @method getCurve
*
* @static
*
* @param {string} curveName dictionary key
* @return {unitCurve} curve function of one numeric variable mapping [0,1]
* to range inside [0,1]
*/
TweenTransition.getCurve = function getCurve(curveName) {
var curve = registeredCurves[curveName];
if (curve !== undefined) { return curve; }
else { throw new Error('curve not registered'); }
};
/**
* Retrieve all available curves.
*
* @method getCurves
*
* @static
*
* @return {object} curve functions of one numeric variable mapping [0,1]
* to range inside [0,1]
*/
TweenTransition.getCurves = function getCurves() {
return registeredCurves;
};
// Interpolate: If a linear function f(0) = a, f(1) = b, then return f(t)
function _interpolate(a, b, t) {
return ((1 - t) * a) + (t * b);
}
function _clone(obj) {
if (obj instanceof Object) {
if (obj instanceof Array) { return obj.slice(0); }
else { return Object.create(obj); }
}
else { return obj; }
}
// Fill in missing properties in "transition" with those in defaultTransition, and
// convert internal named curve to function object, returning as new
// object.
function _normalize(transition, defaultTransition) {
var result = {curve: defaultTransition.curve};
if (defaultTransition.duration) { result.duration = defaultTransition.duration; }
if (defaultTransition.speed) { result.speed = defaultTransition.speed; }
if (transition instanceof Object) {
if (transition.duration !== undefined) { result.duration = transition.duration; }
if (transition.curve) { result.curve = transition.curve; }
if (transition.speed) { result.speed = transition.speed; }
}
if (typeof result.curve === 'string') { result.curve = TweenTransition.getCurve(result.curve); }
return result;
}
/**
* Set internal options, overriding any default options.
*
* @method setOptions
*
*
* @param {Object} options options object
* @param {Object} [options.curve] function mapping [0,1] to [0,1] or identifier
* @param {Number} [options.duration] duration in ms
* @param {Number} [options.speed] speed in pixels per ms
*/
TweenTransition.prototype.setOptions = function setOptions(options) {
if (options.curve !== undefined) { this.options.curve = options.curve; }
if (options.duration !== undefined) { this.options.duration = options.duration; }
if (options.speed !== undefined) { this.options.speed = options.speed; }
};
/**
* Add transition to end state to the queue of pending transitions. Special
* Use: calling without a transition resets the object to that state with
* no pending actions
*
* @method set
*
*
* @param {number|FamousMatrix|Array.Number|Object.<number, number>} endValue
* end state to which we _interpolate
* @param {transition=} transition object of type {duration: number, curve:
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be
* instantaneous.
* @param {function()=} callback Zero-argument function to call on observed
* completion (t=1)
*/
TweenTransition.prototype.set = function set(endValue, transition, callback) {
if (!transition) {
this.reset(endValue);
if (callback) { callback(); }
return;
}
this._startValue = _clone(this.get());
transition = _normalize(transition, this.options);
if (transition.speed) {
var startValue = this._startValue;
if (startValue instanceof Object) {
var variance = 0;
for (var i in startValue) { variance += (endValue[i] - startValue[i]) * (endValue[i] - startValue[i]); }
transition.duration = Math.sqrt(variance) / transition.speed;
}
else {
transition.duration = Math.abs(endValue - startValue) / transition.speed;
}
}
this._startTime = Date.now();
this._endValue = _clone(endValue);
this._startVelocity = _clone(transition.velocity);
this._duration = transition.duration;
this._curve = transition.curve;
this._active = true;
this._callback = callback;
};
/**
* Cancel all transitions and reset to a stable state
*
* @method reset
*
* @param {number|Array.Number|Object.<number, number>} startValue
* starting state
* @param {number} startVelocity
* starting velocity
*/
TweenTransition.prototype.reset = function reset(startValue, startVelocity) {
if (this._callback) {
var callback = this._callback;
this._callback = undefined;
callback();
}
this.state = _clone(startValue);
this.velocity = _clone(startVelocity);
this._startTime = 0;
this._duration = 0;
this._updateTime = 0;
this._startValue = this.state;
this._startVelocity = this.velocity;
this._endValue = this.state;
this._active = false;
};
/**
* Get current velocity
*
* @method getVelocity
*
* @returns {Number} velocity
*/
TweenTransition.prototype.getVelocity = function getVelocity() {
return this.velocity;
};
/**
* Get interpolated state of current action at provided time. If the last
* action has completed, invoke its callback.
*
* @method get
*
*
* @param {number=} timestamp Evaluate the curve at a normalized version of this
* time. If omitted, use current time. (Unix epoch time)
* @return {number|Object.<number|string, number>} beginning state
* _interpolated to this point in time.
*/
TweenTransition.prototype.get = function get(timestamp) {
this.update(timestamp);
return this.state;
};
function _calculateVelocity(current, start, curve, duration, t) {
var velocity;
var eps = 1e-7;
var speed = (curve(t) - curve(t - eps)) / eps;
if (current instanceof Array) {
velocity = [];
for (var i = 0; i < current.length; i++){
if (typeof current[i] === 'number')
{ velocity[i] = speed * (current[i] - start[i]) / duration; }
else
{ velocity[i] = 0; }
}
}
else { velocity = speed * (current - start) / duration; }
return velocity;
}
function _calculateState(start, end, t) {
var state;
if (start instanceof Array) {
state = [];
for (var i = 0; i < start.length; i++) {
if (typeof start[i] === 'number')
{ state[i] = _interpolate(start[i], end[i], t); }
else
{ state[i] = start[i]; }
}
}
else { state = _interpolate(start, end, t); }
return state;
}
/**
* Update internal state to the provided timestamp. This may invoke the last
* callback and begin a new action.
*
* @method update
*
*
* @param {number=} timestamp Evaluate the curve at a normalized version of this
* time. If omitted, use current time. (Unix epoch time)
*/
TweenTransition.prototype.update = function update(timestamp) {
if (!this._active) {
if (this._callback) {
var callback = this._callback;
this._callback = undefined;
callback();
}
return;
}
if (!timestamp) { timestamp = Date.now(); }
if (this._updateTime >= timestamp) { return; }
this._updateTime = timestamp;
var timeSinceStart = timestamp - this._startTime;
if (timeSinceStart >= this._duration) {
this.state = this._endValue;
this.velocity = _calculateVelocity(this.state, this._startValue, this._curve, this._duration, 1);
this._active = false;
}
else if (timeSinceStart < 0) {
this.state = this._startValue;
this.velocity = this._startVelocity;
}
else {
var t = timeSinceStart / this._duration;
this.state = _calculateState(this._startValue, this._endValue, this._curve(t));
this.velocity = _calculateVelocity(this.state, this._startValue, this._curve, this._duration, t);
}
};
/**
* Is there at least one action pending completion?
*
* @method isActive
*
*
* @return {boolean}
*/
TweenTransition.prototype.isActive = function isActive() {
return this._active;
};
/**
* Halt transition at current state and erase all pending actions.
*
* @method halt
*
*/
TweenTransition.prototype.halt = function halt() {
this.reset(this.get());
};
// Register all the default curves
TweenTransition.registerCurve('linear', TweenTransition.Curves.linear);
TweenTransition.registerCurve('easeIn', TweenTransition.Curves.easeIn);
TweenTransition.registerCurve('easeOut', TweenTransition.Curves.easeOut);
TweenTransition.registerCurve('easeInOut', TweenTransition.Curves.easeInOut);
TweenTransition.registerCurve('easeOutBounce', TweenTransition.Curves.easeOutBounce);
TweenTransition.registerCurve('spring', TweenTransition.Curves.spring);
TweenTransition.customCurve = function customCurve(v1, v2) {
v1 = v1 || 0; v2 = v2 || 0;
return function(t) {
return v1*t + (-2*v1 - v2 + 3)*t*t + (v1 + v2 - 2)*t*t*t;
};
};
var TweenTransition_1 = TweenTransition;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: david@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/*eslint-disable new-cap */
/**
* A state maintainer for a smooth transition between
* numerically-specified states. Example numeric states include floats or
* Transform objects.
*
* An initial state is set with the constructor or set(startState). A
* corresponding end state and transition are set with set(endState,
* transition). Subsequent calls to set(endState, transition) begin at
* the last state. Calls to get(timestamp) provide the interpolated state
* along the way.
*
* Note that there is no event loop here - calls to get() are the only way
* to find state projected to the current (or provided) time and are
* the only way to trigger callbacks. Usually this kind of object would
* be part of the render() path of a visible component.
*
* @class Transitionable
* @constructor
* @param {number|Array.Number|Object.<number|string, number>} start
* beginning state
*/
function Transitionable(start) {
this.currentAction = null;
this.actionQueue = [];
this.callbackQueue = [];
this.state = 0;
this.velocity = undefined;
this._callback = undefined;
this._engineInstance = null;
this._currentMethod = null;
this.set(start);
}
var transitionMethods = {};
Transitionable.register = function register(methods) {
var success = true;
for (var method in methods) {
if (!Transitionable.registerMethod(method, methods[method]))
{ success = false; }
}
return success;
};
Transitionable.registerMethod = function registerMethod(name, engineClass) {
if (!(name in transitionMethods)) {
transitionMethods[name] = engineClass;
return true;
}
else { return false; }
};
Transitionable.unregisterMethod = function unregisterMethod(name) {
if (name in transitionMethods) {
delete transitionMethods[name];
return true;
}
else { return false; }
};
function _loadNext() {
if (this._callback) {
var callback = this._callback;
this._callback = undefined;
callback();
}
if (this.actionQueue.length <= 0) {
this.set(this.get()); // no update required
return;
}
this.currentAction = this.actionQueue.shift();
this._callback = this.callbackQueue.shift();
var method = null;
var endValue = this.currentAction[0];
var transition = this.currentAction[1];
if (transition instanceof Object && transition.method) {
method = transition.method;
if (typeof method === 'string') { method = transitionMethods[method]; }
}
else {
method = TweenTransition_1;
}
if (this._currentMethod !== method) {
if (!(endValue instanceof Object) || method.SUPPORTS_MULTIPLE === true || endValue.length <= method.SUPPORTS_MULTIPLE) {
this._engineInstance = new method();
}
else {
this._engineInstance = new MultipleTransition_1(method);
}
this._currentMethod = method;
}
this._engineInstance.reset(this.state, this.velocity);
if (this.velocity !== undefined) { transition.velocity = this.velocity; }
this._engineInstance.set(endValue, transition, _loadNext.bind(this));
}
/**
* Add transition to end state to the queue of pending transitions. Special
* Use: calling without a transition resets the object to that state with
* no pending actions
*
* @method set
*
* @param {number|FamousMatrix|Array.Number|Object.<number, number>} endState
* end state to which we interpolate
* @param {transition=} transition object of type {duration: number, curve:
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be
* instantaneous.
* @param {function()=} callback Zero-argument function to call on observed
* completion (t=1)
*/
Transitionable.prototype.set = function set(endState, transition, callback) {
if (!transition) {
this.reset(endState);
if (callback) { callback(); }
return this;
}
var action = [endState, transition];
this.actionQueue.push(action);
this.callbackQueue.push(callback);
if (!this.currentAction) { _loadNext.call(this); }
return this;
};
/**
* Cancel all transitions and reset to a stable state
*
* @method reset
*
* @param {number|Array.Number|Object.<number, number>} startState
* stable state to set to
*/
Transitionable.prototype.reset = function reset(startState, startVelocity) {
this._currentMethod = null;
this._engineInstance = null;
this._callback = undefined;
this.state = startState;
this.velocity = startVelocity;
this.currentAction = null;
this.actionQueue = [];
this.callbackQueue = [];
};
/**
* Add delay action to the pending action queue queue.
*
* @method delay
*
* @param {number} duration delay time (ms)
* @param {function} callback Zero-argument function to call on observed
* completion (t=1)
*/
Transitionable.prototype.delay = function delay(duration, callback) {
var endValue;
if (this.actionQueue.length) { endValue = this.actionQueue[this.actionQueue.length - 1][0]; }
else if (this.currentAction) { endValue = this.currentAction[0]; }
else { endValue = this.get(); }
return this.set(endValue, { duration: duration,
curve: function() {
return 0;
}},
callback
);
};
/**
* Get interpolated state of current action at provided time. If the last
* action has completed, invoke its callback.
*
* @method get
*
* @param {number=} timestamp Evaluate the curve at a normalized version of this
* time. If omitted, use current time. (Unix epoch time)
* @return {number|Object.<number|string, number>} beginning state
* interpolated to this point in time.
*/
Transitionable.prototype.get = function get(timestamp) {
if (this._engineInstance) {
if (this._engineInstance.getVelocity)
{ this.velocity = this._engineInstance.getVelocity(); }
this.state = this._engineInstance.get(timestamp);
}
return this.state;
};
/**
* Is there at least one action pending completion?
*
* @method isActive
*
* @return {boolean}
*/
Transitionable.prototype.isActive = function isActive() {
return !!this.currentAction;
};
/**
* Halt transition at current state and erase all pending actions.
*
* @method halt
*/
Transitionable.prototype.halt = function halt() {
return this.set(this.get());
};
var Transitionable_1 = Transitionable;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: david@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A library of curves which map an animation explicitly as a function of time.
*
* @class Easing
*/
var Easing = {
/**
* @property inQuad
* @static
*/
inQuad: function(t) {
return t*t;
},
/**
* @property outQuad
* @static
*/
outQuad: function(t) {
return -(t-=1)*t+1;
},
/**
* @property inOutQuad
* @static
*/
inOutQuad: function(t) {
if ((t/=.5) < 1) { return .5*t*t; }
return -.5*((--t)*(t-2) - 1);
},
/**
* @property inCubic
* @static
*/
inCubic: function(t) {
return t*t*t;
},
/**
* @property outCubic
* @static
*/
outCubic: function(t) {
return ((--t)*t*t + 1);
},
/**
* @property inOutCubic
* @static
*/
inOutCubic: function(t) {
if ((t/=.5) < 1) { return .5*t*t*t; }
return .5*((t-=2)*t*t + 2);
},
/**
* @property inQuart
* @static
*/
inQuart: function(t) {
return t*t*t*t;
},
/**
* @property outQuart
* @static
*/
outQuart: function(t) {
return -((--t)*t*t*t - 1);
},
/**
* @property inOutQuart
* @static
*/
inOutQuart: function(t) {
if ((t/=.5) < 1) { return .5*t*t*t*t; }
return -.5 * ((t-=2)*t*t*t - 2);
},
/**
* @property inQuint
* @static
*/
inQuint: function(t) {
return t*t*t*t*t;
},
/**
* @property outQuint
* @static
*/
outQuint: function(t) {
return ((--t)*t*t*t*t + 1);
},
/**
* @property inOutQuint
* @static
*/
inOutQuint: function(t) {
if ((t/=.5) < 1) { return .5*t*t*t*t*t; }
return .5*((t-=2)*t*t*t*t + 2);
},
/**
* @property inSine
* @static
*/
inSine: function(t) {
return -1.0*Math.cos(t * (Math.PI/2)) + 1.0;
},
/**
* @property outSine
* @static
*/
outSine: function(t) {
return Math.sin(t * (Math.PI/2));
},
/**
* @property inOutSine
* @static
*/
inOutSine: function(t) {
return -.5*(Math.cos(Math.PI*t) - 1);
},
/**
* @property inExpo
* @static
*/
inExpo: function(t) {
return (t===0) ? 0.0 : Math.pow(2, 10 * (t - 1));
},
/**
* @property outExpo
* @static
*/
outExpo: function(t) {
return (t===1.0) ? 1.0 : (-Math.pow(2, -10 * t) + 1);
},
/**
* @property inOutExpo
* @static
*/
inOutExpo: function(t) {
if (t===0) { return 0.0; }
if (t===1.0) { return 1.0; }
if ((t/=.5) < 1) { return .5 * Math.pow(2, 10 * (t - 1)); }
return .5 * (-Math.pow(2, -10 * --t) + 2);
},
/**
* @property inCirc
* @static
*/
inCirc: function(t) {
return -(Math.sqrt(1 - t*t) - 1);
},
/**
* @property outCirc
* @static
*/
outCirc: function(t) {
return Math.sqrt(1 - (--t)*t);
},
/**
* @property inOutCirc
* @static
*/
inOutCirc: function(t) {
if ((t/=.5) < 1) { return -.5 * (Math.sqrt(1 - t*t) - 1); }
return .5 * (Math.sqrt(1 - (t-=2)*t) + 1);
},
/**
* @property inElastic
* @static
*/
inElastic: function(t) {
var s=1.70158;var p=0;var a=1.0;
if (t===0) { return 0.0; } if (t===1) { return 1.0; } if (!p) { p=.3; }
s = p/(2*Math.PI) * Math.asin(1.0/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin((t-s)*(2*Math.PI)/ p));
},
/**
* @property outElastic
* @static
*/
outElastic: function(t) {
var s=1.70158;var p=0;var a=1.0;
if (t===0) { return 0.0; } if (t===1) { return 1.0; } if (!p) { p=.3; }
s = p/(2*Math.PI) * Math.asin(1.0/a);
return a*Math.pow(2,-10*t) * Math.sin((t-s)*(2*Math.PI)/p) + 1.0;
},
/**
* @property inOutElastic
* @static
*/
inOutElastic: function(t) {
var s=1.70158;var p=0;var a=1.0;
if (t===0) { return 0.0; } if ((t/=.5)===2) { return 1.0; } if (!p) { p=(.3*1.5); }
s = p/(2*Math.PI) * Math.asin(1.0/a);
if (t < 1) { return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin((t-s)*(2*Math.PI)/p)); }
return a*Math.pow(2,-10*(t-=1)) * Math.sin((t-s)*(2*Math.PI)/p)*.5 + 1.0;
},
/**
* @property inBack
* @static
*/
inBack: function(t, s) {
if (s === undefined) { s = 1.70158; }
return t*t*((s+1)*t - s);
},
/**
* @property outBack
* @static
*/
outBack: function(t, s) {
if (s === undefined) { s = 1.70158; }
return ((--t)*t*((s+1)*t + s) + 1);
},
/**
* @property inOutBack
* @static
*/
inOutBack: function(t, s) {
if (s === undefined) { s = 1.70158; }
if ((t/=.5) < 1) { return .5*(t*t*(((s*=(1.525))+1)*t - s)); }
return .5*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2);
},
/**
* @property inBounce
* @static
*/
inBounce: function(t) {
return 1.0 - Easing.outBounce(1.0-t);
},
/**
* @property outBounce
* @static
*/
outBounce: function(t) {
if (t < (1/2.75)) {
return (7.5625*t*t);
} else if (t < (2/2.75)) {
return (7.5625*(t-=(1.5/2.75))*t + .75);
} else if (t < (2.5/2.75)) {
return (7.5625*(t-=(2.25/2.75))*t + .9375);
} else {
return (7.5625*(t-=(2.625/2.75))*t + .984375);
}
},
/**
* @property inOutBounce
* @static
*/
inOutBounce: function(t) {
if (t < .5) { return Easing.inBounce(t*2) * .5; }
return Easing.outBounce(t*2-1.0) * .5 + .5;
}
};
var Easing_1 = Easing;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: david@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A class for transitioning the state of a Transform by transitioning the
* X, Y, and Z axes of it's translate, scale, skew and rotate components
* independently.
*
* @class TransitionableTransform
* @constructor
*
* @param [transform=Transform.identity] {Transform} The initial transform state
*/
function TransitionableTransform(transform) {
this._final = Transform_1.identity.slice();
this._finalTranslate = [0, 0, 0];
this._finalRotate = [0, 0, 0];
this._finalSkew = [0, 0, 0];
this._finalScale = [1, 1, 1];
this.translate = [];
this.rotate = [];
this.skew = [];
this.scale = [];
for (var i=0; i<3; i+=1) {
this.translate[i] = new Transitionable_1(this._finalTranslate[i]);
this.rotate[i] = new Transitionable_1(this._finalRotate[i]);
this.skew[i] = new Transitionable_1(this._finalSkew[i]);
this.scale[i] = new Transitionable_1(this._finalScale[i]);
}
if (transform) { this.set(transform); }
}
function _build() {
return Transform_1.build({
translate: [this.translate[0].get(), this.translate[1].get(), this.translate[2].get()],
rotate: [this.rotate[0].get(), this.rotate[1].get(), this.rotate[2].get()],
skew: [this.skew[0].get(), this.skew[1].get(), this.skew[2].get()],
scale: [this.scale[0].get(), this.scale[1].get(), this.scale[2].get()]
});
}
function _buildFinal() {
return Transform_1.build({
translate: this._finalTranslate,
rotate: this._finalRotate,
skew: this._finalSkew,
scale: this._finalScale
});
}
function _countOfType(array, type) {
var count = 0;
for (var i=0; i<array.length; i+=1) {
if (typeof array[i] === type+'') {
count+=1;
}
}
return count;
}
/**
* An optimized way of setting only the translation component of a Transform. Axes who's values are null will not be affected.
*
* @method setTranslate
* @chainable
*
* @param translate {Array} New translation state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setTranslate = function setTranslate(translate, transition, callback) {
var numberOfAxes = _countOfType(translate, 'number');
var _callback = callback ? Utility_1.after(numberOfAxes, callback) : null;
for (var i=0; i<translate.length; i+=1) {
if (typeof translate[i] === 'number') {
this.translate[i].set(translate[i], transition, _callback);
this._finalTranslate[i] = translate[i];
}
}
this._final = _buildFinal.call(this);
return this;
};
/**
* Translate only along the X axis of the translation component of a Transform.
*
* @method setTranslateX
* @chainable
*
* @param translate {Number} New translation state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setTranslateX = function setTranslateX(translate, transition, callback) {
this.translate[0].set(translate, transition, callback);
this._finalTranslate[0] = translate;
this._final = _buildFinal.call(this);
return this;
};
/**
* Translate only along the Y axis of the translation component of a Transform.
*
* @method setTranslateY
* @chainable
*
* @param translate {Number} New translation state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setTranslateY = function setTranslateY(translate, transition, callback) {
this.translate[1].set(translate, transition, callback);
this._finalTranslate[1] = translate;
this._final = _buildFinal.call(this);
return this;
};
/**
* Translate only along the Z axis of the translation component of a Transform.
*
* @method setTranslateZ
* @chainable
*
* @param translate {Number} New translation state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setTranslateZ = function setTranslateZ(translate, transition, callback) {
this.translate[2].set(translate, transition, callback);
this._finalTranslate[2] = translate;
this._final = _buildFinal.call(this);
return this;
};
/**
* An optimized way of setting only the scale component of a Transform. Axes who's values are null will not be affected.
*
* @method setScale
* @chainable
*
* @param scale {Array} New scale state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setScale = function setScale(scale, transition, callback) {
var numberOfAxes = _countOfType(scale, 'number');
var _callback = callback ? Utility_1.after(numberOfAxes, callback) : null;
for (var i=0; i<scale.length; i+=1) {
if (typeof scale[i] === 'number') {
this.scale[i].set(scale[i], transition, _callback);
this._finalScale[i] = scale[i];
}
}
this._final = _buildFinal.call(this);
return this;
};
/**
* Scale only along the X axis of the scale component of a Transform.
*
* @method setScaleX
* @chainable
*
* @param scale {Number} New scale state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setScaleX = function setScaleX(scale, transition, callback) {
this.scale[0].set(scale, transition, callback);
this._finalScale[0] = scale;
this._final = _buildFinal.call(this);
return this;
};
/**
* Scale only along the Y axis of the scale component of a Transform.
*
* @method setScaleY
* @chainable
*
* @param scale {Number} New scale state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setScaleY = function setScaleY(scale, transition, callback) {
this.scale[1].set(scale, transition, callback);
this._finalScale[1] = scale;
this._final = _buildFinal.call(this);
return this;
};
/**
* Scale only along the Z axis of the scale component of a Transform.
*
* @method setScaleZ
* @chainable
*
* @param scale {Number} New scale state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setScaleZ = function setScaleZ(scale, transition, callback) {
this.scale[2].set(scale, transition, callback);
this._finalScale[2] = scale;
this._final = _buildFinal.call(this);
return this;
};
/**
* An optimized way of setting only the rotational component of a Transform. Axes who's values are null will not be affected.
*
* @method setRotate
* @chainable
*
* @param eulerAngles {Array} Euler angles for new rotation state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setRotate = function setRotate(eulerAngles, transition, callback) {
var numberOfAxes = _countOfType(eulerAngles, 'number');
var _callback = callback ? Utility_1.after(numberOfAxes, callback) : null;
for (var i=0; i<eulerAngles.length; i+=1) {
if (typeof eulerAngles[i] === 'number') {
this.rotate[i].set(eulerAngles[i], transition, _callback);
this._finalRotate[i] = eulerAngles[i];
}
}
this._final = _buildFinal.call(this);
return this;
};
/**
* Rotate only about the X axis of the rotational component of a Transform.
*
* @method setScaleX
* @chainable
*
* @param eulerAngle {Number} New rotational state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setRotateX = function setRotateX(eulerAngle, transition, callback) {
this.rotate[0].set(eulerAngle, transition, callback);
this._finalRotate[0] = eulerAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* Rotate only about the Y axis of the rotational component of a Transform.
*
* @method setScaleY
* @chainable
*
* @param eulerAngle {Number} New rotational state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setRotateY = function setRotateY(eulerAngle, transition, callback) {
this.rotate[1].set(eulerAngle, transition, callback);
this._finalRotate[1] = eulerAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* Rotate only about the Z axis of the rotational component of a Transform.
*
* @method setScaleZ
* @chainable
*
* @param eulerAngle {Number} New rotational state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setRotateZ = function setRotateZ(eulerAngle, transition, callback) {
this.rotate[2].set(eulerAngle, transition, callback);
this._finalRotate[2] = eulerAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* An optimized way of setting only the skew component of a Transform. Axes who's values are null will not be affected.
*
* @method setSkew
* @chainable
*
* @param skewAngles {Array} New skew state. Axes who's values are null will not be affected.
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setSkew = function setSkew(skewAngles, transition, callback) {
var numberOfAxes = _countOfType(skewAngles, 'number');
var _callback = callback ? Utility_1.after(numberOfAxes, callback) : null;
for (var i=0; i<skewAngles.length; i+=1) {
if (typeof skewAngles[i] === 'number') {
this.skew[i].set(skewAngles[i], transition, _callback);
this._finalSkew[i] = skewAngles[i];
}
}
this._final = _buildFinal.call(this);
return this;
};
/**
* Skew only about the X axis of the skew component of a Transform.
*
* @method setSkewX
* @chainable
*
* @param skewAngle {Number} New skew state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setSkewX = function setSkewX(skewAngle, transition, callback) {
this.skew[0].set(skewAngle, transition, callback);
this._finalSkew[0] = skewAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* Skew only about the Y axis of the skew component of a Transform.
*
* @method setSkewY
* @chainable
*
* @param skewAngle {Number} New skew state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setSkewY = function setSkewY(skewAngle, transition, callback) {
this.skew[1].set(skewAngle, transition, callback);
this._finalSkew[1] = skewAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* Skew only about the Z axis of the skew component of a Transform.
*
* @method setSkewZ
* @chainable
*
* @param skewAngle {Number} New skew state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.setSkewZ = function setSkewZ(skewAngle, transition, callback) {
this.skew[2].set(skewAngle, transition, callback);
this._finalSkew[2] = skewAngle;
this._final = _buildFinal.call(this);
return this;
};
/**
* Setter for a TransitionableTransform with optional parameters to transition
* between Transforms. Animates all axes of all components.
*
* @method set
* @chainable
*
* @param transform {Array} New transform state
* @param [transition] {Object} Transition definition
* @param [callback] {Function} Callback
* @return {TransitionableTransform}
*/
TransitionableTransform.prototype.set = function set(transform, transition, callback) {
var components = Transform_1.interpret(transform);
this._finalTranslate = components.translate;
this._finalRotate = components.rotate;
this._finalSkew = components.skew;
this._finalScale = components.scale;
this._final = transform;
var _callback = callback ? Utility_1.after(12, callback) : null;
for (var i=0; i<3; i+=1) {
this.translate[i].set(components.translate[i], transition, _callback);
this.rotate[i].set(components.rotate[i], transition, _callback);
this.skew[i].set(components.skew[i], transition, _callback);
this.scale[i].set(components.scale[i], transition, _callback);
}
return this;
};
/**
* Sets the default transition to use for transitioning betwen Transform states
*
* @method setDefaultTransition
*
* @param transition {Object} Transition definition
*/
TransitionableTransform.prototype.setDefaultTransition = function setDefaultTransition(transition) {
for (var i=0; i<3; i+=1) {
this.translate[i].setDefault(transition);
this.rotate[i].setDefault(transition);
this.skew[i].setDefault(transition);
this.scale[i].setDefault(transition);
}
};
/**
* Getter. Returns the current state of the Transform
*
* @method get
*
* @return {Transform}
*/
TransitionableTransform.prototype.get = function get() {
if (this.isActive()) {
return _build.call(this);
}
else { return this._final; }
};
/**
* Get the destination state of the Transform
*
* @method getFinal
*
* @return Transform {Transform}
*/
TransitionableTransform.prototype.getFinal = function getFinal() {
return this._final;
};
/**
* Determine if the TransitionableTransform is currently transitioning
*
* @method isActive
*
* @return {Boolean}
*/
TransitionableTransform.prototype.isActive = function isActive() {
var isActive = false;
for (var i=0; i<3; i+=1) {
if (
this.translate[i].isActive()
|| this.rotate[i].isActive()
|| this.skew[i].isActive()
|| this.scale[i].isActive()
) {
isActive = true; break;
}
}
return isActive;
};
/**
* Halts the transition
*
* @method halt
*/
TransitionableTransform.prototype.halt = function halt() {
for (var i=0; i<3; i+=1) {
this.translate[i].halt();
this.rotate[i].halt();
this.skew[i].halt();
this.scale[i].halt();
this._finalTranslate[i] = this.translate[i].get();
this._finalRotate[i] = this.rotate[i].get();
this._finalSkew[i] = this.skew[i].get();
this._finalScale[i] = this.scale[i].get();
}
this._final = this.get();
return this;
};
var TransitionableTransform_1 = TransitionableTransform;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/* TODO: remove these dependencies when deprecation complete */
/**
*
* A collection of visual changes to be
* applied to another renderable component. This collection includes a
* transform matrix, an opacity constant, a size, an origin specifier.
* Modifier objects can be added to any RenderNode or object
* capable of displaying renderables. The Modifier's children and descendants
* are transformed by the amounts specified in the Modifier's properties.
*
* @class Modifier
* @constructor
* @param {Object} [options] overrides of default options
* @param {Transform} [options.transform] affine transformation matrix
* @param {Number} [options.opacity]
* @param {Array.Number} [options.origin] origin adjustment
* @param {Array.Number} [options.size] size to apply to descendants
*/
function Modifier(options) {
this._transformGetter = null;
this._opacityGetter = null;
this._originGetter = null;
this._alignGetter = null;
this._sizeGetter = null;
this._proportionGetter = null;
/* TODO: remove this when deprecation complete */
this._legacyStates = {};
this._output = {
transform: Transform_1.identity,
opacity: 1,
origin: null,
align: null,
size: null,
proportions: null,
target: null
};
if (options) {
if (options.transform) { this.transformFrom(options.transform); }
if (options.opacity !== undefined) { this.opacityFrom(options.opacity); }
if (options.origin) { this.originFrom(options.origin); }
if (options.align) { this.alignFrom(options.align); }
if (options.size) { this.sizeFrom(options.size); }
if (options.proportions) { this.proportionsFrom(options.proportions); }
}
}
/**
* Function, object, or static transform matrix which provides the transform.
* This is evaluated on every tick of the engine.
*
* @method transformFrom
*
* @param {Object} transform transform provider object
* @return {Modifier} this
*/
Modifier.prototype.transformFrom = function transformFrom(transform) {
if (transform instanceof Function) { this._transformGetter = transform; }
else if (transform instanceof Object && transform.get) { this._transformGetter = transform.get.bind(transform); }
else {
this._transformGetter = null;
this._output.transform = transform;
}
return this;
};
/**
* Set function, object, or number to provide opacity, in range [0,1].
*
* @method opacityFrom
*
* @param {Object} opacity provider object
* @return {Modifier} this
*/
Modifier.prototype.opacityFrom = function opacityFrom(opacity) {
if (opacity instanceof Function) { this._opacityGetter = opacity; }
else if (opacity instanceof Object && opacity.get) { this._opacityGetter = opacity.get.bind(opacity); }
else {
this._opacityGetter = null;
this._output.opacity = opacity;
}
return this;
};
/**
* Set function, object, or numerical array to provide origin, as [x,y],
* where x and y are in the range [0,1].
*
* @method originFrom
*
* @param {Object} origin provider object
* @return {Modifier} this
*/
Modifier.prototype.originFrom = function originFrom(origin) {
if (origin instanceof Function) { this._originGetter = origin; }
else if (origin instanceof Object && origin.get) { this._originGetter = origin.get.bind(origin); }
else {
this._originGetter = null;
this._output.origin = origin;
}
return this;
};
/**
* Set function, object, or numerical array to provide align, as [x,y],
* where x and y are in the range [0,1].
*
* @method alignFrom
*
* @param {Object} align provider object
* @return {Modifier} this
*/
Modifier.prototype.alignFrom = function alignFrom(align) {
if (align instanceof Function) { this._alignGetter = align; }
else if (align instanceof Object && align.get) { this._alignGetter = align.get.bind(align); }
else {
this._alignGetter = null;
this._output.align = align;
}
return this;
};
/**
* Set function, object, or numerical array to provide size, as [width, height].
*
* @method sizeFrom
*
* @param {Object} size provider object
* @return {Modifier} this
*/
Modifier.prototype.sizeFrom = function sizeFrom(size) {
if (size instanceof Function) { this._sizeGetter = size; }
else if (size instanceof Object && size.get) { this._sizeGetter = size.get.bind(size); }
else {
this._sizeGetter = null;
this._output.size = size;
}
return this;
};
/**
* Set function, object, or numerical array to provide proportions, as [percent of width, percent of height].
*
* @method proportionsFrom
*
* @param {Object} proportions provider object
* @return {Modifier} this
*/
Modifier.prototype.proportionsFrom = function proportionsFrom(proportions) {
if (proportions instanceof Function) { this._proportionGetter = proportions; }
else if (proportions instanceof Object && proportions.get) { this._proportionGetter = proportions.get.bind(proportions); }
else {
this._proportionGetter = null;
this._output.proportions = proportions;
}
return this;
};
/**
* Deprecated: Prefer transformFrom with static Transform, or use a TransitionableTransform.
* @deprecated
* @method setTransform
*
* @param {Transform} transform Transform to transition to
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setTransform = function setTransform(transform, transition, callback) {
if (transition || this._legacyStates.transform) {
if (!this._legacyStates.transform) {
this._legacyStates.transform = new TransitionableTransform_1(this._output.transform);
}
if (!this._transformGetter) { this.transformFrom(this._legacyStates.transform); }
this._legacyStates.transform.set(transform, transition, callback);
return this;
}
else { return this.transformFrom(transform); }
};
/**
* Deprecated: Prefer opacityFrom with static opacity array, or use a Transitionable with that opacity.
* @deprecated
* @method setOpacity
*
* @param {Number} opacity Opacity value to transition to.
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setOpacity = function setOpacity(opacity, transition, callback) {
if (transition || this._legacyStates.opacity) {
if (!this._legacyStates.opacity) {
this._legacyStates.opacity = new Transitionable_1(this._output.opacity);
}
if (!this._opacityGetter) { this.opacityFrom(this._legacyStates.opacity); }
return this._legacyStates.opacity.set(opacity, transition, callback);
}
else { return this.opacityFrom(opacity); }
};
/**
* Deprecated: Prefer originFrom with static origin array, or use a Transitionable with that origin.
* @deprecated
* @method setOrigin
*
* @param {Array.Number} origin two element array with values between 0 and 1.
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setOrigin = function setOrigin(origin, transition, callback) {
/* TODO: remove this if statement when deprecation complete */
if (transition || this._legacyStates.origin) {
if (!this._legacyStates.origin) {
this._legacyStates.origin = new Transitionable_1(this._output.origin || [0, 0]);
}
if (!this._originGetter) { this.originFrom(this._legacyStates.origin); }
this._legacyStates.origin.set(origin, transition, callback);
return this;
}
else { return this.originFrom(origin); }
};
/**
* Deprecated: Prefer alignFrom with static align array, or use a Transitionable with that align.
* @deprecated
* @method setAlign
*
* @param {Array.Number} align two element array with values between 0 and 1.
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setAlign = function setAlign(align, transition, callback) {
/* TODO: remove this if statement when deprecation complete */
if (transition || this._legacyStates.align) {
if (!this._legacyStates.align) {
this._legacyStates.align = new Transitionable_1(this._output.align || [0, 0]);
}
if (!this._alignGetter) { this.alignFrom(this._legacyStates.align); }
this._legacyStates.align.set(align, transition, callback);
return this;
}
else { return this.alignFrom(align); }
};
/**
* Deprecated: Prefer sizeFrom with static origin array, or use a Transitionable with that size.
* @deprecated
* @method setSize
* @param {Array.Number} size two element array of [width, height]
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setSize = function setSize(size, transition, callback) {
if (size && (transition || this._legacyStates.size)) {
if (!this._legacyStates.size) {
this._legacyStates.size = new Transitionable_1(this._output.size || [0, 0]);
}
if (!this._sizeGetter) { this.sizeFrom(this._legacyStates.size); }
this._legacyStates.size.set(size, transition, callback);
return this;
}
else { return this.sizeFrom(size); }
};
/**
* Deprecated: Prefer proportionsFrom with static origin array, or use a Transitionable with those proportions.
* @deprecated
* @method setProportions
* @param {Array.Number} proportions two element array of [percent of width, percent of height]
* @param {Transitionable} transition Valid transitionable object
* @param {Function} callback callback to call after transition completes
* @return {Modifier} this
*/
Modifier.prototype.setProportions = function setProportions(proportions, transition, callback) {
if (proportions && (transition || this._legacyStates.proportions)) {
if (!this._legacyStates.proportions) {
this._legacyStates.proportions = new Transitionable_1(this._output.proportions || [0, 0]);
}
if (!this._proportionGetter) { this.proportionsFrom(this._legacyStates.proportions); }
this._legacyStates.proportions.set(proportions, transition, callback);
return this;
}
else { return this.proportionsFrom(proportions); }
};
/**
* Deprecated: Prefer to stop transform in your provider object.
* @deprecated
* @method halt
*/
Modifier.prototype.halt = function halt() {
if (this._legacyStates.transform) { this._legacyStates.transform.halt(); }
if (this._legacyStates.opacity) { this._legacyStates.opacity.halt(); }
if (this._legacyStates.origin) { this._legacyStates.origin.halt(); }
if (this._legacyStates.align) { this._legacyStates.align.halt(); }
if (this._legacyStates.size) { this._legacyStates.size.halt(); }
if (this._legacyStates.proportions) { this._legacyStates.proportions.halt(); }
this._transformGetter = null;
this._opacityGetter = null;
this._originGetter = null;
this._alignGetter = null;
this._sizeGetter = null;
this._proportionGetter = null;
};
/**
* Deprecated: Prefer to use your provided transform or output of your transform provider.
* @deprecated
* @method getTransform
* @return {Object} transform provider object
*/
Modifier.prototype.getTransform = function getTransform() {
return this._transformGetter();
};
/**
* Deprecated: Prefer to determine the end state of your transform from your transform provider
* @deprecated
* @method getFinalTransform
* @return {Transform} transform matrix
*/
Modifier.prototype.getFinalTransform = function getFinalTransform() {
return this._legacyStates.transform ? this._legacyStates.transform.getFinal() : this._output.transform;
};
/**
* Deprecated: Prefer to use your provided opacity or output of your opacity provider.
* @deprecated
* @method getOpacity
* @return {Object} opacity provider object
*/
Modifier.prototype.getOpacity = function getOpacity() {
return this._opacityGetter();
};
/**
* Deprecated: Prefer to use your provided origin or output of your origin provider.
* @deprecated
* @method getOrigin
* @return {Object} origin provider object
*/
Modifier.prototype.getOrigin = function getOrigin() {
return this._originGetter();
};
/**
* Deprecated: Prefer to use your provided align or output of your align provider.
* @deprecated
* @method getAlign
* @return {Object} align provider object
*/
Modifier.prototype.getAlign = function getAlign() {
return this._alignGetter();
};
/**
* Deprecated: Prefer to use your provided size or output of your size provider.
* @deprecated
* @method getSize
* @return {Object} size provider object
*/
Modifier.prototype.getSize = function getSize() {
return this._sizeGetter ? this._sizeGetter() : this._output.size;
};
/**
* Deprecated: Prefer to use your provided proportions or output of your proportions provider.
* @deprecated
* @method getProportions
* @return {Object} proportions provider object
*/
Modifier.prototype.getProportions = function getProportions() {
return this._proportionGetter ? this._proportionGetter() : this._output.proportions;
};
// call providers on tick to receive render spec elements to apply
function _update() {
if (this._transformGetter) { this._output.transform = this._transformGetter(); }
if (this._opacityGetter) { this._output.opacity = this._opacityGetter(); }
if (this._originGetter) { this._output.origin = this._originGetter(); }
if (this._alignGetter) { this._output.align = this._alignGetter(); }
if (this._sizeGetter) { this._output.size = this._sizeGetter(); }
if (this._proportionGetter) { this._output.proportions = this._proportionGetter(); }
}
/**
* Return render spec for this Modifier, applying to the provided
* target component. This is similar to render() for Surfaces.
*
* @private
* @method modify
*
* @param {Object} target (already rendered) render spec to
* which to apply the transform.
* @return {Object} render spec for this Modifier, including the
* provided target
*/
Modifier.prototype.modify = function modify(target) {
_update.call(this);
this._output.target = target;
return this._output;
};
var Modifier_1 = Modifier;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A singleton that maintains a global registry of Surfaces.
* Private.
*
* @private
* @static
* @class Entity
*/
var entities = [];
/**
* Get entity from global index.
*
* @private
* @method get
* @param {Number} id entity registration id
* @return {Surface} entity in the global index
*/
function get(id) {
return entities[id];
}
/**
* Overwrite entity in the global index
*
* @private
* @method set
* @param {Number} id entity registration id
* @param {Surface} entity to add to the global index
*/
function set(id, entity) {
entities[id] = entity;
}
/**
* Add entity to global index
*
* @private
* @method register
* @param {Surface} entity to add to global index
* @return {Number} new id
*/
function register(entity) {
var id = entities.length;
set(id, entity);
return id;
}
/**
* Remove entity from global index
*
* @private
* @method unregister
* @param {Number} id entity registration id
*/
function unregister(id) {
set(id, null);
}
var Entity = {
register: register,
unregister: unregister,
get: get,
set: set
};
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
*
* This object translates the rendering instructions ("render specs")
* that renderable components generate into document update
* instructions ("update specs"). Private.
*
* @private
* @class SpecParser
* @constructor
*/
function SpecParser() {
this.result = {};
}
SpecParser._instance = new SpecParser();
/**
* Convert a render spec coming from the context's render chain to an
* update spec for the update chain. This is the only major entry point
* for a consumer of this class.
*
* @method parse
* @static
* @private
*
* @param {renderSpec} spec input render spec
* @param {Object} context context to do the parse in
* @return {Object} the resulting update spec (if no callback
* specified, else none)
*/
SpecParser.parse = function parse(spec, context) {
return SpecParser._instance.parse(spec, context);
};
/**
* Convert a renderSpec coming from the context's render chain to an update
* spec for the update chain. This is the only major entrypoint for a
* consumer of this class.
*
* @method parse
*
* @private
* @param {renderSpec} spec input render spec
* @param {Context} context
* @return {updateSpec} the resulting update spec
*/
SpecParser.prototype.parse = function parse(spec, context) {
this.reset();
this._parseSpec(spec, context, Transform_1.identity);
return this.result;
};
/**
* Prepare SpecParser for re-use (or first use) by setting internal state
* to blank.
*
* @private
* @method reset
*/
SpecParser.prototype.reset = function reset() {
this.result = {};
};
// Multiply matrix M by vector v
function _vecInContext(v, m) {
return [
v[0] * m[0] + v[1] * m[4] + v[2] * m[8],
v[0] * m[1] + v[1] * m[5] + v[2] * m[9],
v[0] * m[2] + v[1] * m[6] + v[2] * m[10]
];
}
var _zeroZero = [0, 0];
// From the provided renderSpec tree, recursively compose opacities,
// origins, transforms, and sizes corresponding to each surface id from
// the provided renderSpec tree structure. On completion, those
// properties of 'this' object should be ready to use to build an
// updateSpec.
SpecParser.prototype._parseSpec = function _parseSpec(spec, parentContext, sizeContext) {
var id;
var target;
var transform;
var opacity;
var origin;
var align;
var size;
if (typeof spec === 'number') {
id = spec;
transform = parentContext.transform;
align = parentContext.align || _zeroZero;
if (parentContext.size && align && (align[0] || align[1])) {
var alignAdjust = [align[0] * parentContext.size[0], align[1] * parentContext.size[1], 0];
transform = Transform_1.thenMove(transform, _vecInContext(alignAdjust, sizeContext));
}
this.result[id] = {
transform: transform,
opacity: parentContext.opacity,
origin: parentContext.origin || _zeroZero,
align: parentContext.align || _zeroZero,
size: parentContext.size
};
}
else if (!spec) { // placed here so 0 will be cached earlier
return;
}
else if (spec instanceof Array) {
for (var i = 0; i < spec.length; i++) {
this._parseSpec(spec[i], parentContext, sizeContext);
}
}
else {
target = spec.target;
transform = parentContext.transform;
opacity = parentContext.opacity;
origin = parentContext.origin;
align = parentContext.align;
size = parentContext.size;
var nextSizeContext = sizeContext;
if (spec.opacity !== undefined) { opacity = parentContext.opacity * spec.opacity; }
if (spec.transform) { transform = Transform_1.multiply(parentContext.transform, spec.transform); }
if (spec.origin) {
origin = spec.origin;
nextSizeContext = parentContext.transform;
}
if (spec.align) { align = spec.align; }
if (spec.size || spec.proportions) {
var parentSize = size;
size = [size[0], size[1]];
if (spec.size) {
if (spec.size[0] !== undefined) { size[0] = spec.size[0]; }
if (spec.size[1] !== undefined) { size[1] = spec.size[1]; }
}
if (spec.proportions) {
if (spec.proportions[0] !== undefined) { size[0] = size[0] * spec.proportions[0]; }
if (spec.proportions[1] !== undefined) { size[1] = size[1] * spec.proportions[1]; }
}
if (parentSize) {
if (align && (align[0] || align[1])) { transform = Transform_1.thenMove(transform, _vecInContext([align[0] * parentSize[0], align[1] * parentSize[1], 0], sizeContext)); }
if (origin && (origin[0] || origin[1])) { transform = Transform_1.moveThen([-origin[0] * size[0], -origin[1] * size[1], 0], transform); }
}
nextSizeContext = parentContext.transform;
origin = null;
align = null;
}
this._parseSpec(target, {
transform: transform,
opacity: opacity,
origin: origin,
align: align,
size: size
}, nextSizeContext);
}
};
var SpecParser_1 = SpecParser;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A wrapper for inserting a renderable component (like a Modifer or
* Surface) into the render tree.
*
* @class RenderNode
* @constructor
*
* @param {Object} object Target renderable component
*/
function RenderNode(object) {
this._object = null;
this._child = null;
this._hasMultipleChildren = false;
this._isRenderable = false;
this._isModifier = false;
this._resultCache = {};
this._prevResults = {};
this._childResult = null;
if (object) { this.set(object); }
}
/**
* Append a renderable to the list of this node's children.
* This produces a new RenderNode in the tree.
* Note: Does not double-wrap if child is a RenderNode already.
*
* @method add
* @param {Object} child renderable object
* @return {RenderNode} new render node wrapping child
*/
RenderNode.prototype.add = function add(child) {
var childNode = (child instanceof RenderNode) ? child : new RenderNode(child);
if (this._child instanceof Array) { this._child.push(childNode); }
else if (this._child) {
this._child = [this._child, childNode];
this._hasMultipleChildren = true;
this._childResult = []; // to be used later
}
else { this._child = childNode; }
return childNode;
};
/**
* Return the single wrapped object. Returns null if this node has multiple child nodes.
*
* @method get
*
* @return {Ojbect} contained renderable object
*/
RenderNode.prototype.get = function get() {
return this._object || (this._hasMultipleChildren ? null : (this._child ? this._child.get() : null));
};
/**
* Overwrite the list of children to contain the single provided object
*
* @method set
* @param {Object} child renderable object
* @return {RenderNode} this render node, or child if it is a RenderNode
*/
RenderNode.prototype.set = function set(child) {
this._childResult = null;
this._hasMultipleChildren = false;
this._isRenderable = child.render ? true : false;
this._isModifier = child.modify ? true : false;
this._object = child;
this._child = null;
if (child instanceof RenderNode) { return child; }
else { return this; }
};
/**
* Get render size of contained object.
*
* @method getSize
* @return {Array.Number} size of this or size of single child.
*/
RenderNode.prototype.getSize = function getSize() {
var result = null;
var target = this.get();
if (target && target.getSize) { result = target.getSize(); }
if (!result && this._child && this._child.getSize) { result = this._child.getSize(); }
return result;
};
// apply results of rendering this subtree to the document
function _applyCommit(spec, context, cacheStorage) {
var result = SpecParser_1.parse(spec, context);
var keys = Object.keys(result);
for (var i = 0; i < keys.length; i++) {
var id = keys[i];
var childNode = Entity.get(id);
var commitParams = result[id];
commitParams.allocator = context.allocator;
var commitResult = childNode.commit(commitParams);
if (commitResult) { _applyCommit(commitResult, context, cacheStorage); }
else { cacheStorage[id] = commitParams; }
}
}
/**
* Commit the content change from this node to the document.
*
* @private
* @method commit
* @param {Context} context render context
*/
RenderNode.prototype.commit = function commit(context) {
// free up some divs from the last loop
var prevKeys = Object.keys(this._prevResults);
for (var i = 0; i < prevKeys.length; i++) {
var id = prevKeys[i];
if (this._resultCache[id] === undefined) {
var object = Entity.get(id);
if (object.cleanup) { object.cleanup(context.allocator); }
}
}
this._prevResults = this._resultCache;
this._resultCache = {};
_applyCommit(this.render(), context, this._resultCache);
};
/**
* Generate a render spec from the contents of the wrapped component.
*
* @private
* @method render
*
* @return {Object} render specification for the component subtree
* only under this node.
*/
RenderNode.prototype.render = function render() {
if (this._isRenderable) { return this._object.render(); }
var result = null;
if (this._hasMultipleChildren) {
result = this._childResult;
var children = this._child;
for (var i = 0; i < children.length; i++) {
result[i] = children[i].render();
}
}
else if (this._child) { result = this._child.render(); }
return this._isModifier ? this._object.modify(result) : result;
};
var RenderNode_1 = RenderNode;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* EventEmitter represents a channel for events.
*
* @class EventEmitter
* @constructor
*/
function EventEmitter() {
this.listeners = {};
this._owner = this;
}
/**
* Trigger an event, sending to all downstream handlers
* listening for provided 'type' key.
*
* @method emit
*
* @param {string} type event type key (for example, 'click')
* @param {Object} event event data
* @return {EventHandler} this
*/
EventEmitter.prototype.emit = function emit(type, event) {
var handlers = this.listeners[type];
if (handlers) {
for (var i = 0; i < handlers.length; i++) {
handlers[i].call(this._owner, event);
}
}
return this;
};
/**
* Bind a callback function to an event type handled by this object.
*
* @method "on"
*
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} handler callback
* @return {EventHandler} this
*/
EventEmitter.prototype.on = function on(type, handler) {
if (!(type in this.listeners)) { this.listeners[type] = []; }
var index = this.listeners[type].indexOf(handler);
if (index < 0) { this.listeners[type].push(handler); }
return this;
};
/**
* Alias for "on".
* @method addListener
*/
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
/**
* Unbind an event by type and handler.
* This undoes the work of "on".
*
* @method removeListener
*
* @param {string} type event type key (for example, 'click')
* @param {function} handler function object to remove
* @return {EventEmitter} this
*/
EventEmitter.prototype.removeListener = function removeListener(type, handler) {
var listener = this.listeners[type];
if (listener !== undefined) {
var index = listener.indexOf(handler);
if (index >= 0) { listener.splice(index, 1); }
}
return this;
};
/**
* Call event handlers with this set to owner.
*
* @method bindThis
*
* @param {Object} owner object this EventEmitter belongs to
*/
EventEmitter.prototype.bindThis = function bindThis(owner) {
this._owner = owner;
};
var EventEmitter_1 = EventEmitter;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* EventHandler forwards received events to a set of provided callback functions.
* It allows events to be captured, processed, and optionally piped through to other event handlers.
*
* @class EventHandler
* @extends EventEmitter
* @constructor
*/
function EventHandler() {
EventEmitter_1.apply(this, arguments);
this.downstream = []; // downstream event handlers
this.downstreamFn = []; // downstream functions
this.upstream = []; // upstream event handlers
this.upstreamListeners = {}; // upstream listeners
}
EventHandler.prototype = Object.create(EventEmitter_1.prototype);
EventHandler.prototype.constructor = EventHandler;
/**
* Assign an event handler to receive an object's input events.
*
* @method setInputHandler
* @static
*
* @param {Object} object object to mix trigger, subscribe, and unsubscribe functions into
* @param {EventHandler} handler assigned event handler
*/
EventHandler.setInputHandler = function setInputHandler(object, handler) {
object.trigger = handler.trigger.bind(handler);
if (handler.subscribe && handler.unsubscribe) {
object.subscribe = handler.subscribe.bind(handler);
object.unsubscribe = handler.unsubscribe.bind(handler);
}
};
/**
* Assign an event handler to receive an object's output events.
*
* @method setOutputHandler
* @static
*
* @param {Object} object object to mix pipe, unpipe, on, addListener, and removeListener functions into
* @param {EventHandler} handler assigned event handler
*/
EventHandler.setOutputHandler = function setOutputHandler(object, handler) {
if (handler instanceof EventHandler) { handler.bindThis(object); }
object.pipe = handler.pipe.bind(handler);
object.unpipe = handler.unpipe.bind(handler);
object.on = handler.on.bind(handler);
object.addListener = object.on;
object.removeListener = handler.removeListener.bind(handler);
};
/**
* Trigger an event, sending to all downstream handlers
* listening for provided 'type' key.
*
* @method emit
*
* @param {string} type event type key (for example, 'click')
* @param {Object} event event data
* @return {EventHandler} this
*/
EventHandler.prototype.emit = function emit(type, event) {
EventEmitter_1.prototype.emit.apply(this, arguments);
var i = 0;
for (i = 0; i < this.downstream.length; i++) {
if (this.downstream[i].trigger) { this.downstream[i].trigger(type, event); }
}
for (i = 0; i < this.downstreamFn.length; i++) {
this.downstreamFn[i](type, event);
}
return this;
};
/**
* Alias for emit
* @method addListener
*/
EventHandler.prototype.trigger = EventHandler.prototype.emit;
/**
* Add event handler object to set of downstream handlers.
*
* @method pipe
*
* @param {EventHandler} target event handler target object
* @return {EventHandler} passed event handler
*/
EventHandler.prototype.pipe = function pipe(target) {
if (target.subscribe instanceof Function) { return target.subscribe(this); }
var downstreamCtx = (target instanceof Function) ? this.downstreamFn : this.downstream;
var index = downstreamCtx.indexOf(target);
if (index < 0) { downstreamCtx.push(target); }
if (target instanceof Function) { target('pipe', null); }
else if (target.trigger) { target.trigger('pipe', null); }
return target;
};
/**
* Remove handler object from set of downstream handlers.
* Undoes work of "pipe".
*
* @method unpipe
*
* @param {EventHandler} target target handler object
* @return {EventHandler} provided target
*/
EventHandler.prototype.unpipe = function unpipe(target) {
if (target.unsubscribe instanceof Function) { return target.unsubscribe(this); }
var downstreamCtx = (target instanceof Function) ? this.downstreamFn : this.downstream;
var index = downstreamCtx.indexOf(target);
if (index >= 0) {
downstreamCtx.splice(index, 1);
if (target instanceof Function) { target('unpipe', null); }
else if (target.trigger) { target.trigger('unpipe', null); }
return target;
}
else { return false; }
};
/**
* Bind a callback function to an event type handled by this object.
*
* @method "on"
*
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} handler callback
* @return {EventHandler} this
*/
EventHandler.prototype.on = function on(type, handler) {
EventEmitter_1.prototype.on.apply(this, arguments);
if (!(type in this.upstreamListeners)) {
var upstreamListener = this.trigger.bind(this, type);
this.upstreamListeners[type] = upstreamListener;
for (var i = 0; i < this.upstream.length; i++) {
this.upstream[i].on(type, upstreamListener);
}
}
return this;
};
/**
* Alias for "on"
* @method addListener
*/
EventHandler.prototype.addListener = EventHandler.prototype.on;
/**
* Listen for events from an upstream event handler.
*
* @method subscribe
*
* @param {EventEmitter} source source emitter object
* @return {EventHandler} this
*/
EventHandler.prototype.subscribe = function subscribe(source) {
var index = this.upstream.indexOf(source);
if (index < 0) {
this.upstream.push(source);
for (var type in this.upstreamListeners) {
source.on(type, this.upstreamListeners[type]);
}
}
return this;
};
/**
* Stop listening to events from an upstream event handler.
*
* @method unsubscribe
*
* @param {EventEmitter} source source emitter object
* @return {EventHandler} this
*/
EventHandler.prototype.unsubscribe = function unsubscribe(source) {
var index = this.upstream.indexOf(source);
if (index >= 0) {
this.upstream.splice(index, 1);
for (var type in this.upstreamListeners) {
source.removeListener(type, this.upstreamListeners[type]);
}
}
return this;
};
var EventHandler_1 = EventHandler;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* Internal helper object to Context that handles the process of
* creating and allocating DOM elements within a managed div.
* Private.
*
* @class ElementAllocator
* @constructor
* @private
* @param {Node} container document element in which Famo.us content will be inserted
*/
function ElementAllocator(container) {
if (!container) { container = document.createDocumentFragment(); }
this.container = container;
this.detachedNodes = {};
this.nodeCount = 0;
}
/**
* Move the document elements from their original container to a new one.
*
* @private
* @method migrate
*
* @param {Node} container document element to which Famo.us content will be migrated
*/
ElementAllocator.prototype.migrate = function migrate(container) {
var oldContainer = this.container;
if (container === oldContainer) { return; }
if (oldContainer instanceof DocumentFragment) {
container.appendChild(oldContainer);
}
else {
while (oldContainer.hasChildNodes()) {
container.appendChild(oldContainer.firstChild);
}
}
this.container = container;
};
/**
* Allocate an element of specified type from the pool.
*
* @private
* @method allocate
*
* @param {string} type type of element, e.g. 'div'
* @return {Node} allocated document element
*/
ElementAllocator.prototype.allocate = function allocate(type) {
type = type.toLowerCase();
if (!(type in this.detachedNodes)) { this.detachedNodes[type] = []; }
var nodeStore = this.detachedNodes[type];
var result;
if (nodeStore.length > 0) {
result = nodeStore.pop();
}
else {
result = document.createElement(type);
this.container.appendChild(result);
}
this.nodeCount++;
return result;
};
/**
* De-allocate an element of specified type to the pool.
*
* @private
* @method deallocate
*
* @param {Node} element document element to deallocate
*/
ElementAllocator.prototype.deallocate = function deallocate(element) {
var nodeType = element.nodeName.toLowerCase();
var nodeStore = this.detachedNodes[nodeType];
nodeStore.push(element);
this.nodeCount--;
};
/**
* Get count of total allocated nodes in the document.
*
* @private
* @method getNodeCount
*
* @return {Number} total node count
*/
ElementAllocator.prototype.getNodeCount = function getNodeCount() {
return this.nodeCount;
};
var ElementAllocator_1 = ElementAllocator;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
var _zeroZero$1 = [0, 0];
var usePrefix = !('perspective' in document.documentElement.style);
function _getElementSize() {
var element = this.container;
return [element.clientWidth, element.clientHeight];
}
var _setPerspective = usePrefix ? function(element, perspective) {
element.style.webkitPerspective = perspective ? perspective.toFixed() + 'px' : '';
} : function(element, perspective) {
element.style.perspective = perspective ? perspective.toFixed() + 'px' : '';
};
/**
* The top-level container for a Famous-renderable piece of the document.
* It is directly updated by the process-wide Engine object, and manages one
* render tree root, which can contain other renderables.
*
* @class Context
* @constructor
* @private
* @param {Node} container Element in which content will be inserted
*/
function Context(container) {
this.container = container;
this._allocator = new ElementAllocator_1(container);
this._node = new RenderNode_1();
this._eventOutput = new EventHandler_1();
this._size = _getElementSize.call(this);
this._perspectiveState = new Transitionable_1(0);
this._perspective = undefined;
this._nodeContext = {
allocator: this._allocator,
transform: Transform_1.identity,
opacity: 1,
origin: _zeroZero$1,
align: _zeroZero$1,
size: this._size
};
this._eventOutput.on('resize', function() {
this.setSize(_getElementSize.call(this));
}.bind(this));
}
// Note: Unused
Context.prototype.getAllocator = function getAllocator() {
return this._allocator;
};
/**
* Add renderables to this Context's render tree.
*
* @method add
*
* @param {Object} obj renderable object
* @return {RenderNode} RenderNode wrapping this object, if not already a RenderNode
*/
Context.prototype.add = function add(obj) {
return this._node.add(obj);
};
/**
* Move this Context to another containing document element.
*
* @method migrate
*
* @param {Node} container Element to which content will be migrated
*/
Context.prototype.migrate = function migrate(container) {
if (container === this.container) { return; }
this.container = container;
this._allocator.migrate(container);
};
/**
* Gets viewport size for Context.
*
* @method getSize
*
* @return {Array.Number} viewport size as [width, height]
*/
Context.prototype.getSize = function getSize() {
return this._size;
};
/**
* Sets viewport size for Context.
*
* @method setSize
*
* @param {Array.Number} size [width, height]. If unspecified, use size of root document element.
*/
Context.prototype.setSize = function setSize(size) {
if (!size) { size = _getElementSize.call(this); }
this._size[0] = size[0];
this._size[1] = size[1];
};
/**
* Commit this Context's content changes to the document.
*
* @private
* @method update
* @param {Object} contextParameters engine commit specification
*/
Context.prototype.update = function update(contextParameters) {
if (contextParameters) {
if (contextParameters.transform) { this._nodeContext.transform = contextParameters.transform; }
if (contextParameters.opacity) { this._nodeContext.opacity = contextParameters.opacity; }
if (contextParameters.origin) { this._nodeContext.origin = contextParameters.origin; }
if (contextParameters.align) { this._nodeContext.align = contextParameters.align; }
if (contextParameters.size) { this._nodeContext.size = contextParameters.size; }
}
var perspective = this._perspectiveState.get();
if (perspective !== this._perspective) {
_setPerspective(this.container, perspective);
this._perspective = perspective;
}
this._node.commit(this._nodeContext);
};
/**
* Get current perspective of this context in pixels.
*
* @method getPerspective
* @return {Number} depth perspective in pixels
*/
Context.prototype.getPerspective = function getPerspective() {
return this._perspectiveState.get();
};
/**
* Set current perspective of this context in pixels.
*
* @method setPerspective
* @param {Number} perspective in pixels
* @param {Object} [transition] Transitionable object for applying the change
* @param {function(Object)} callback function called on completion of transition
*/
Context.prototype.setPerspective = function setPerspective(perspective, transition, callback) {
return this._perspectiveState.set(perspective, transition, callback);
};
/**
* Trigger an event, sending to all downstream handlers
* listening for provided 'type' key.
*
* @method emit
*
* @param {string} type event type key (for example, 'click')
* @param {Object} event event data
* @return {EventHandler} this
*/
Context.prototype.emit = function emit(type, event) {
return this._eventOutput.emit(type, event);
};
/**
* Bind a callback function to an event type handled by this object.
*
* @method "on"
*
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} handler callback
* @return {EventHandler} this
*/
Context.prototype.on = function on(type, handler) {
return this._eventOutput.on(type, handler);
};
/**
* Unbind an event by type and handler.
* This undoes the work of "on".
*
* @method removeListener
*
* @param {string} type event type key (for example, 'click')
* @param {function} handler function object to remove
* @return {EventHandler} internal event handler object (for chaining)
*/
Context.prototype.removeListener = function removeListener(type, handler) {
return this._eventOutput.removeListener(type, handler);
};
/**
* Add event handler object to set of downstream handlers.
*
* @method pipe
*
* @param {EventHandler} target event handler target object
* @return {EventHandler} passed event handler
*/
Context.prototype.pipe = function pipe(target) {
return this._eventOutput.pipe(target);
};
/**
* Remove handler object from set of downstream handlers.
* Undoes work of "pipe".
*
* @method unpipe
*
* @param {EventHandler} target target handler object
* @return {EventHandler} provided target
*/
Context.prototype.unpipe = function unpipe(target) {
return this._eventOutput.unpipe(target);
};
var Context_1 = Context;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A collection of methods for setting options which can be extended
* onto other classes.
*
*
* **** WARNING ****
* You can only pass through objects that will compile into valid JSON.
*
* Valid options:
* Strings,
* Arrays,
* Objects,
* Numbers,
* Nested Objects,
* Nested Arrays.
*
* This excludes:
* Document Fragments,
* Functions
* @class OptionsManager
* @constructor
* @param {Object} value options dictionary
*/
function OptionsManager(value) {
this._value = value;
this.eventOutput = null;
}
/**
* Create options manager from source dictionary with arguments overriden by patch dictionary.
*
* @static
* @method OptionsManager.patch
*
* @param {Object} source source arguments
* @param {...Object} data argument additions and overwrites
* @return {Object} source object
*/
OptionsManager.patch = function patchObject(source, data) {
var manager = new OptionsManager(source);
for (var i = 1; i < arguments.length; i++) { manager.patch(arguments[i]); }
return source;
};
function _createEventOutput() {
this.eventOutput = new EventHandler_1();
this.eventOutput.bindThis(this);
EventHandler_1.setOutputHandler(this, this.eventOutput);
}
/**
* Create OptionsManager from source with arguments overriden by patches.
* Triggers 'change' event on this object's event handler if the state of
* the OptionsManager changes as a result.
*
* @method patch
*
* @param {...Object} arguments list of patch objects
* @return {OptionsManager} this
*/
OptionsManager.prototype.patch = function patch() {
var myState = this._value;
for (var i = 0; i < arguments.length; i++) {
var data = arguments[i];
for (var k in data) {
if ((k in myState) && (data[k] && data[k].constructor === Object) && (myState[k] && myState[k].constructor === Object)) {
if (!myState.hasOwnProperty(k)) { myState[k] = Object.create(myState[k]); }
this.key(k).patch(data[k]);
if (this.eventOutput) { this.eventOutput.emit('change', {id: k, value: this.key(k).value()}); }
}
else { this.set(k, data[k]); }
}
}
return this;
};
/**
* Alias for patch
*
* @method setOptions
*
*/
OptionsManager.prototype.setOptions = OptionsManager.prototype.patch;
/**
* Return OptionsManager based on sub-object retrieved by key
*
* @method key
*
* @param {string} identifier key
* @return {OptionsManager} new options manager with the value
*/
OptionsManager.prototype.key = function key(identifier) {
var result = new OptionsManager(this._value[identifier]);
if (!(result._value instanceof Object) || result._value instanceof Array) { result._value = {}; }
return result;
};
/**
* Look up value by key or get the full options hash
* @method get
*
* @param {string} key key
* @return {Object} associated object or full options hash
*/
OptionsManager.prototype.get = function get(key) {
return key ? this._value[key] : this._value;
};
/**
* Alias for get
* @method getOptions
*/
OptionsManager.prototype.getOptions = OptionsManager.prototype.get;
/**
* Set key to value. Outputs 'change' event if a value is overwritten.
*
* @method set
*
* @param {string} key key string
* @param {Object} value value object
* @return {OptionsManager} new options manager based on the value object
*/
OptionsManager.prototype.set = function set(key, value) {
var originalValue = this.get(key);
this._value[key] = value;
if (this.eventOutput && value !== originalValue) { this.eventOutput.emit('change', {id: key, value: value}); }
return this;
};
/**
* Bind a callback function to an event type handled by this object.
*
* @method "on"
*
* @param {string} type event type key (for example, 'change')
* @param {function(string, Object)} handler callback
* @return {EventHandler} this
*/
OptionsManager.prototype.on = function on() {
_createEventOutput.call(this);
return this.on.apply(this, arguments);
};
/**
* Unbind an event by type and handler.
* This undoes the work of "on".
*
* @method removeListener
*
* @param {string} type event type key (for example, 'change')
* @param {function} handler function object to remove
* @return {EventHandler} internal event handler object (for chaining)
*/
OptionsManager.prototype.removeListener = function removeListener() {
_createEventOutput.call(this);
return this.removeListener.apply(this, arguments);
};
/**
* Add event handler object to set of downstream handlers.
*
* @method pipe
*
* @param {EventHandler} target event handler target object
* @return {EventHandler} passed event handler
*/
OptionsManager.prototype.pipe = function pipe() {
_createEventOutput.call(this);
return this.pipe.apply(this, arguments);
};
/**
* Remove handler object from set of downstream handlers.
* Undoes work of "pipe"
*
* @method unpipe
*
* @param {EventHandler} target target handler object
* @return {EventHandler} provided target
*/
OptionsManager.prototype.unpipe = function unpipe() {
_createEventOutput.call(this);
return this.unpipe.apply(this, arguments);
};
var OptionsManager_1 = OptionsManager;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* The singleton object initiated upon process
* startup which manages all active Context instances, runs
* the render dispatch loop, and acts as a listener and dispatcher
* for events. All methods are therefore static.
*
* On static initialization, window.requestAnimationFrame is called with
* the event loop function.
*
* Note: Any window in which Engine runs will prevent default
* scrolling behavior on the 'touchmove' event.
*
* @static
* @class Engine
*/
var Engine = {};
var contexts = [];
var nextTickQueue = [];
var currentFrame = 0;
var nextTickFrame = 0;
var deferQueue = [];
var lastTime = Date.now();
var frameTime;
var frameTimeLimit;
var loopEnabled = true;
var eventForwarders = {};
var eventHandler = new EventHandler_1();
var options = {
containerType: 'div',
containerClass: 'famous-container',
fpsCap: undefined,
runLoop: true,
appMode: true
};
var optionsManager = new OptionsManager_1(options);
/** @const */
var MAX_DEFER_FRAME_TIME = 10;
/**
* Inside requestAnimationFrame loop, step() is called, which:
* calculates current FPS (throttling loop if it is over limit set in setFPSCap),
* emits dataless 'prerender' event on start of loop,
* calls in order any one-shot functions registered by nextTick on last loop,
* calls Context.update on all Context objects registered,
* and emits dataless 'postrender' event on end of loop.
*
* @static
* @private
* @method step
*/
Engine.step = function step() {
currentFrame++;
nextTickFrame = currentFrame;
var currentTime = Date.now();
// skip frame if we're over our framerate cap
if (frameTimeLimit && currentTime - lastTime < frameTimeLimit) { return; }
var i = 0;
frameTime = currentTime - lastTime;
lastTime = currentTime;
eventHandler.emit('prerender');
// empty the queue
var numFunctions = nextTickQueue.length;
while (numFunctions--) { (nextTickQueue.shift())(currentFrame); }
// limit total execution time for deferrable functions
while (deferQueue.length && (Date.now() - currentTime) < MAX_DEFER_FRAME_TIME) {
deferQueue.shift().call(this);
}
for (i = 0; i < contexts.length; i++) { contexts[i].update(); }
eventHandler.emit('postrender');
};
// engage requestAnimationFrame
function loop() {
if (options.runLoop) {
Engine.step();
window.requestAnimationFrame(loop);
}
else { loopEnabled = false; }
}
window.requestAnimationFrame(loop);
//
// Upon main document window resize (unless on an "input" HTML element):
// scroll to the top left corner of the window,
// and for each managed Context: emit the 'resize' event and update its size.
// @param {Object=} event document event
//
function handleResize(event) {
for (var i = 0; i < contexts.length; i++) {
contexts[i].emit('resize');
}
eventHandler.emit('resize');
}
window.addEventListener('resize', handleResize, false);
handleResize();
/**
* Initialize famous for app mode
*
* @static
* @private
* @method initialize
*/
function initialize() {
// prevent scrolling via browser
window.addEventListener('touchmove', function(event) {
event.preventDefault();
}, true);
addRootClasses();
}
var initialized = false;
function addRootClasses() {
if (!document.body) {
Engine.nextTick(addRootClasses);
return;
}
document.body.classList.add('famous-root');
document.documentElement.classList.add('famous-root');
}
/**
* Add event handler object to set of downstream handlers.
*
* @method pipe
*
* @param {EventHandler} target event handler target object
* @return {EventHandler} passed event handler
*/
Engine.pipe = function pipe(target) {
if (target.subscribe instanceof Function) { return target.subscribe(Engine); }
else { return eventHandler.pipe(target); }
};
/**
* Remove handler object from set of downstream handlers.
* Undoes work of "pipe".
*
* @method unpipe
*
* @param {EventHandler} target target handler object
* @return {EventHandler} provided target
*/
Engine.unpipe = function unpipe(target) {
if (target.unsubscribe instanceof Function) { return target.unsubscribe(Engine); }
else { return eventHandler.unpipe(target); }
};
/**
* Bind a callback function to an event type handled by this object.
*
* @static
* @method "on"
*
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} handler callback
* @return {EventHandler} this
*/
Engine.on = function on(type, handler) {
if (!(type in eventForwarders)) {
eventForwarders[type] = eventHandler.emit.bind(eventHandler, type);
addEngineListener(type, eventForwarders[type]);
}
return eventHandler.on(type, handler);
};
function addEngineListener(type, forwarder) {
if (!document.body) {
Engine.nextTick(addEventListener.bind(this, type, forwarder));
return;
}
document.body.addEventListener(type, forwarder);
}
/**
* Trigger an event, sending to all downstream handlers
* listening for provided 'type' key.
*
* @method emit
*
* @param {string} type event type key (for example, 'click')
* @param {Object} event event data
* @return {EventHandler} this
*/
Engine.emit = function emit(type, event) {
return eventHandler.emit(type, event);
};
/**
* Unbind an event by type and handler.
* This undoes the work of "on".
*
* @static
* @method removeListener
*
* @param {string} type event type key (for example, 'click')
* @param {function} handler function object to remove
* @return {EventHandler} internal event handler object (for chaining)
*/
Engine.removeListener = function removeListener(type, handler) {
return eventHandler.removeListener(type, handler);
};
/**
* Return the current calculated frames per second of the Engine.
*
* @static
* @method getFPS
*
* @return {Number} calculated fps
*/
Engine.getFPS = function getFPS() {
return 1000 / frameTime;
};
/**
* Set the maximum fps at which the system should run. If internal render
* loop is called at a greater frequency than this FPSCap, Engine will
* throttle render and update until this rate is achieved.
*
* @static
* @method setFPSCap
*
* @param {Number} fps maximum frames per second
*/
Engine.setFPSCap = function setFPSCap(fps) {
frameTimeLimit = Math.floor(1000 / fps);
};
/**
* Return engine options.
*
* @static
* @method getOptions
* @param {string} key
* @return {Object} engine options
*/
Engine.getOptions = function getOptions(key) {
return optionsManager.getOptions(key);
};
/**
* Set engine options
*
* @static
* @method setOptions
*
* @param {Object} [options] overrides of default options
* @param {Number} [options.fpsCap] maximum fps at which the system should run
* @param {boolean} [options.runLoop=true] whether the run loop should continue
* @param {string} [options.containerType="div"] type of container element. Defaults to 'div'.
* @param {string} [options.containerClass="famous-container"] type of container element. Defaults to 'famous-container'.
*/
Engine.setOptions = function setOptions(options) {
return optionsManager.setOptions.apply(optionsManager, arguments);
};
/**
* Creates a new Context for rendering and event handling with
* provided document element as top of each tree. This will be tracked by the
* process-wide Engine.
*
* @static
* @method createContext
*
* @param {Node} el will be top of Famo.us document element tree
* @return {Context} new Context within el
*/
Engine.createContext = function createContext(el) {
if (!initialized && options.appMode) { Engine.nextTick(initialize); }
var needMountContainer = false;
if (!el) {
el = document.createElement(options.containerType);
el.classList.add(options.containerClass);
needMountContainer = true;
}
var context = new Context_1(el);
Engine.registerContext(context);
if (needMountContainer) { mount(context, el); }
return context;
};
function mount(context, el) {
if (!document.body) {
Engine.nextTick(mount.bind(this, context, el));
return;
}
document.body.appendChild(el);
context.emit('resize');
}
/**
* Registers an existing context to be updated within the run loop.
*
* @static
* @method registerContext
*
* @param {Context} context Context to register
* @return {FamousContext} provided context
*/
Engine.registerContext = function registerContext(context) {
contexts.push(context);
return context;
};
/**
* Returns a list of all contexts.
*
* @static
* @method getContexts
* @return {Array} contexts that are updated on each tick
*/
Engine.getContexts = function getContexts() {
return contexts;
};
/**
* Removes a context from the run loop. Note: this does not do any
* cleanup.
*
* @static
* @method deregisterContext
*
* @param {Context} context Context to deregister
*/
Engine.deregisterContext = function deregisterContext(context) {
var i = contexts.indexOf(context);
if (i >= 0) { contexts.splice(i, 1); }
};
/**
* Queue a function to be executed on the next tick of the
* Engine.
*
* @static
* @method nextTick
*
* @param {function(Object)} fn function accepting window object
*/
Engine.nextTick = function nextTick(fn) {
nextTickQueue.push(fn);
};
/**
* Queue a function to be executed sometime soon, at a time that is
* unlikely to affect frame rate.
*
* @static
* @method defer
*
* @param {Function} fn
*/
Engine.defer = function defer(fn) {
deferQueue.push(fn);
};
optionsManager.on('change', function(data) {
if (data.id === 'fpsCap') { Engine.setFPSCap(data.value); }
else if (data.id === 'runLoop') {
// kick off the loop only if it was stopped
if (!loopEnabled && data.value) {
loopEnabled = true;
window.requestAnimationFrame(loop);
}
}
});
var Engine_1 = Engine;
/*
* @overview Utility functions used by infamous.
*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* Creates a [famous/src/core/Context](#famous/src/core/Context) having the specified 3D perspective.
*
* @param {Number} perspective The integer amount of perspective to apply to the `Context`.
* @returns {module: famous/src/core/Context} The `Context` with the applied perspective.
*/
function contextWithPerspective(perspective) {
const context = Engine_1.createContext();
context.setPerspective(perspective);
return context;
}
function simpleExtend(object, ...others) {
others.forEach(function(other) {
for (const prop in other) {
object[prop] = other[prop];
}
});
}
var utils = Object.freeze({
contextWithPerspective: contextWithPerspective,
simpleExtend: simpleExtend
});
// Polyfill for Function.name on browsers that do not support it (IE):
// See: http://stackoverflow.com/questions/6903762/function-name-not-supported-in-ie
if (!(function f() {}).name) {
Object.defineProperty(Function.prototype, "name", {
get: function () {
var name = this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
// For better performance only parse once, and then cache the
// result through a new accessor for repeated access.
Object.defineProperty(this, "name", { value: name });
return name;
}
});
}
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* Molecules are the basic building blocks of all UI components. Molecules
* extend [famous/src/core/RenderNode](#famous/src/core/RenderNode), so they can be
* added to any `RenderNode` of a famo.us render tree, and by default they will
* also accept anything that a normal Famo.us `RenderNode` can accept via the
* `add` method. Classes that extend from `Molecule` might override
* `RenderNode.add` in order to accept things like arrays of renderables in
* stead of a single renderable.
*
* Molecules encapsulate the basic things you need for a component -- a
* [famous/src/transitions/TransitionableTransform](#famous/src/transitions/TransitionableTransform)
* for positioning things in space, and a [famous/src/core/EventHandler](#famous/src/core/EventHandler)
* for capturing user interaction -- exposing a unified API for working with these
* things. For now, [famous/src/core/Modifier](#famous/src/core/Modifier) is used as the interface
* for applying transforms and sizing, but this will change in Mixed Mode
* Famo.us.
*
* All components extend Molecule, but at the same time they can also use any
* number of Molecules internally to do nice things like create layouts and
* position multiple things in space.
*
* @class Molecule
* @extends {module: famous/src/core/RenderNode}
*/
var Molecule = (function (RenderNode) {
function Molecule(initialOptions) {
RenderNode.call(this);
// "private" stuff. Not really, but regard it like so. For example, if
// you see something like obj._.someVariable then you're accessing
// internal stuff that wasn't designed to be accessed directly, and any
// problem you enounter with that is your own problem. :)
//
// TODO: Use a WeakMap to store these at some point.
this._ = {
options: {}, // set and get with this.options
defaultOptions: {}
};
// Add default values for this Molecule
// TODO: Make default options static for the class.
simpleExtend(this._.defaultOptions, {
align: [0.5,0.5],
origin: [0.5,0.5],
transform: new TransitionableTransform_1,
handler: new EventHandler_1
});
// set the user's initial options. This automatically creates
// this.modifier, and adds it to this (don't forget, *this* is a
// RenderNode, so a Molecule can add things to itself).
//
// NOTE: this.options is a setter property. This statement applies all
// relevant properties to this.modifier.
this.options = initialOptions;
}
if ( RenderNode ) Molecule.__proto__ = RenderNode;
Molecule.prototype = Object.create( RenderNode && RenderNode.prototype );
Molecule.prototype.constructor = Molecule;
var prototypeAccessors = { options: {},transform: {} };
/**
* @property {Object} options The Molecule's options, which get applied to
* `this.modifier`. This may change with Mixed Mode. Setting this property
* overrides existing options. To extend existing options with new options,
* use `setOptions` instead. Unspecified options will be set to their default
* values.
*
* Note: Anytime `this.options` is assigned a new value, `this.modifier` is set
* to a new [famous/src/core/Modifier](#famous/src/core/Modifier).
*/
prototypeAccessors.options.set = function (newOptions) {
this.resetOptions();
this.setOptions(newOptions);
};
prototypeAccessors.options.get = function () {
return this._.options;
};
/**
* @property {module: famous/src/transitions/TransitionableTransform} transform
* The transform of this `Molecule`. The default is a
* [famous/src/transitions/TransitionableTransform](#famous/src/transitions/TransitionableTransform).
* Setting this property automatically puts the new transform into effect.
* See [famous/src/core/Modifier.transformFrom](#famous/src/core/Modifier.transformFrom).
*/
prototypeAccessors.transform.set = function (newTransform) {
this.setOptions({transform: newTransform});
};
prototypeAccessors.transform.get = function () {
return this.options.transform;
};
/**
* Compounds `newOptions` into the existing options, similar to extending an
* object and overriding only the desired properties. To override all
* options with a set of new options, set `this.options` directly.
*
* An example of setting just a single option without erasing other options:
*
* ```js
* const myMolecule = new Molecule()
* myMolecule.setOptions({
* align: [0.2, 0.8]
* })
* ```
*
* @param {Object} newOptions An object containing the new options to apply to this `Molecule`.
*/
Molecule.prototype.setOptions = function setOptions (newOptions) {
if (typeof newOptions == 'undefined' || newOptions.constructor.name != "Object")
{ newOptions = {}; }
for (const prop in newOptions) {
// Subject to change when Famo.us API changes.
if (Modifier_1.prototype[''+prop+'From']) {
this.modifier[''+prop+'From'](newOptions[prop]);
}
this._.options[prop] = newOptions[prop];
}
};
/**
* Sets all options back to their defaults.
*
* Note: Anytime this is called, `this.modifier` is set to a new
* [famous/src/core/Modifier](#famous/src/core/Modifier) having the default
* options.
*/
Molecule.prototype.resetOptions = function resetOptions () {
this.modifier = new Modifier_1();
this.set(this.modifier);
this.setOptions(this._.defaultOptions);
};
/**
* Forwards events from this Molecule's [famous/src/core/EventHandler](#famous/src/core/EventHandler) to the given
* target, which can be another `EventHandler` or `Molecule`.
*
* This method is equivalent to [famous/src/core/EventHandler.pipe](#famous/src/core/EventHandler.pipe),
* acting upon `this.handler`.
*
* TODO v0.1.0: Let this method accept a `Molecule`, then stop doing `pipe(this._.handler)` in other places
*/
Molecule.prototype.pipe = function pipe () {
const args = Array.prototype.splice.call(arguments, 0);
return this.options.handler.pipe.apply(this.options.handler, args);
};
/**
* Stops events from this Molecule's [famous/src/core/EventHandler](#famous/src/core/EventHandler)
* from being sent to the given target.
*
* This method is equivalent to [famous/src/core/EventHandler.unpipe](#famous/src/core/EventHandler.unpipe),
* acting upon `this.handler`.
*
* TODO v0.1.0: Let this method accept a `Molecule`, then stop doing `pipe(this.options.handler)` in other places
*/
Molecule.prototype.unpipe = function unpipe () {
const args = Array.prototype.splice.call(arguments, 0);
return this.options.handler.unpipe.apply(this.options.handler, args);
};
/**
* Register an event handler for the specified event.
* See [famous/src/core/EventHandler.on](#famous/src/core/EventHandler.on).
*/
Molecule.prototype.on = function on () {
const args = Array.prototype.splice.call(arguments, 0);
return this.options.handler.on.apply(this.options.handler, args);
};
/**
* Unregister an event handler for the specified event.
* See [famous/src/core/EventHandler.off](#famous/src/core/EventHandler.off).
*/
Molecule.prototype.off = function off () {
const args = Array.prototype.splice.call(arguments, 0);
return this.options.handler.on.apply(this.options.handler, args);
};
Object.defineProperties( Molecule.prototype, prototypeAccessors );
return Molecule;
}(RenderNode_1));
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function unwrapExports (x) {
return x && x.__esModule ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var forLength_1 = createCommonjsModule(function (module, exports) {
"use strict";
// loop for a given length, performing action each loop iteration. action receives the index of the loop.
exports.forLength = forLength;
function forLength(length, action) {
for (var i = 0; i < length; i += 1) {
action(i);
}
}
exports["default"] = forLength;
exports.__esModule = true;
});
var forLength = unwrapExports(forLength_1);
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* A scenegraph tree with a variable number of leaf node Modifiers (the grid
* cells) that are arranged in a grid. Add any [famous/src/core/RenderNode](#famous/src/core/RenderNode)-compatible
* item to each leafnode of the grid.
*
* TODO: Use Molecule instead of Modifier for the grid cells.
* TODO: Add an options parameter, that the Molecule constructor will handle.
*
* @class Grid
* @extends Molecule
*/
var Grid = (function (Molecule$$1) {
function Grid(columns, rows, size) {
Molecule$$1.call(this, {size: size});
this.columns = columns;
this.rows = rows;
this.cellNodes = [];
if (typeof this.options.size === 'undefined') { this.setOptions({size: [undefined, undefined]}); }
forLength(this.columns*this.rows, this._createGridCell.bind(this));
}
if ( Molecule$$1 ) Grid.__proto__ = Molecule$$1;
Grid.prototype = Object.create( Molecule$$1 && Molecule$$1.prototype );
Grid.prototype.constructor = Grid;
/**
* Creates a grid cell at the given index.
*
* @private
* @param {Number} index The integer index of the grid cell.
*/
Grid.prototype._createGridCell = function _createGridCell (index) {
const column = index % this.columns;
const row = Math.floor(index / this.columns);
let cellSize = null;
if (typeof this.options.size[0] != 'undefined' && typeof this.options.size[1] != 'undefined') {
cellSize = [];
cellSize[0] = this.options.size[0]/this.columns;
cellSize[1] = this.options.size[1]/this.rows;
}
const mod = new Modifier_1({
align: [0,0],
origin: [0,0],
size: cellSize? [cellSize[0], cellSize[1]]: [undefined, undefined],
transform: Transform_1.translate(column*cellSize[0],row*cellSize[1],0)
});
const mod2 = new Modifier_1({
//transform: Transform.rotateY(Math.PI/10),
align: [0.5,0.5],
origin: [0.5,0.5]
});
// FIXME: ^^^ Why do I need an extra Modifier to align stuff in the middle of the grid cells?????
this.cellNodes.push(this.add(mod).add(mod2));
};
/**
* Sets the items to be layed out in the grid.
*
* @param {Array} children An array of [famous/src/core/RenderNode](#famous/src/core/RenderNode)-compatible items.
*/
Grid.prototype.setChildren = function setChildren (children) {
forLength(this.columns*this.rows, function(index) {
//this.cellNodes[index].set(null); // TODO: how do we erase previous children?
this.cellNodes[index].add(children[index]);
}.bind(this));
return this;
};
return Grid;
}(Molecule));
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
var usePrefix$1 = !('transform' in document.documentElement.style);
var devicePixelRatio = window.devicePixelRatio || 1;
/**
* A base class for viewable content and event
* targets inside a Famo.us application, containing a renderable document
* fragment. Like an HTML div, it can accept internal markup,
* properties, classes, and handle events.
*
* @class ElementOutput
* @constructor
*
* @param {Node} element document parent of this container
*/
function ElementOutput(element) {
this._matrix = null;
this._opacity = 1;
this._origin = null;
this._size = null;
this._eventOutput = new EventHandler_1();
this._eventOutput.bindThis(this);
/** @ignore */
this.eventForwarder = function eventForwarder(event) {
this._eventOutput.emit(event.type, event);
}.bind(this);
this.id = Entity.register(this);
this._element = null;
this._sizeDirty = false;
this._originDirty = false;
this._transformDirty = false;
this._invisible = false;
if (element) { this.attach(element); }
}
/**
* Bind a callback function to an event type handled by this object.
*
* @method "on"
*
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} fn handler callback
* @return {EventHandler} this
*/
ElementOutput.prototype.on = function on(type, fn) {
if (this._element) { this._element.addEventListener(type, this.eventForwarder); }
this._eventOutput.on(type, fn);
};
/**
* Unbind an event by type and handler.
* This undoes the work of "on"
*
* @method removeListener
* @param {string} type event type key (for example, 'click')
* @param {function(string, Object)} fn handler
*/
ElementOutput.prototype.removeListener = function removeListener(type, fn) {
this._eventOutput.removeListener(type, fn);
};
/**
* Trigger an event, sending to all downstream handlers
* listening for provided 'type' key.
*
* @method emit
*
* @param {string} type event type key (for example, 'click')
* @param {Object} [event] event data
* @return {EventHandler} this
*/
ElementOutput.prototype.emit = function emit(type, event) {
if (event && !event.origin) { event.origin = this; }
var handled = this._eventOutput.emit(type, event);
if (handled && event && event.stopPropagation) { event.stopPropagation(); }
return handled;
};
/**
* Add event handler object to set of downstream handlers.
*
* @method pipe
*
* @param {EventHandler} target event handler target object
* @return {EventHandler} passed event handler
*/
ElementOutput.prototype.pipe = function pipe(target) {
return this._eventOutput.pipe(target);
};
/**
* Remove handler object from set of downstream handlers.
* Undoes work of "pipe"
*
* @method unpipe
*
* @param {EventHandler} target target handler object
* @return {EventHandler} provided target
*/
ElementOutput.prototype.unpipe = function unpipe(target) {
return this._eventOutput.unpipe(target);
};
/**
* Return spec for this surface. Note that for a base surface, this is
* simply an id.
*
* @method render
* @private
* @return {Object} render spec for this surface (spec id)
*/
ElementOutput.prototype.render = function render() {
return this.id;
};
// Attach Famous event handling to document events emanating from target
// document element. This occurs just after attachment to the document.
// Calling this enables methods like #on and #pipe.
function _addEventListeners(target) {
for (var i in this._eventOutput.listeners) {
target.addEventListener(i, this.eventForwarder);
}
}
// Detach Famous event handling from document events emanating from target
// document element. This occurs just before detach from the document.
function _removeEventListeners(target) {
for (var i in this._eventOutput.listeners) {
target.removeEventListener(i, this.eventForwarder);
}
}
/**
* Return a Matrix's webkit css representation to be used with the
* CSS3 -webkit-transform style.
* Example: -webkit-transform: matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,716,243,0,1)
*
* @method _formatCSSTransform
* @private
* @param {FamousMatrix} m matrix
* @return {string} matrix3d CSS style representation of the transform
*/
function _formatCSSTransform(m) {
m[12] = Math.round(m[12] * devicePixelRatio) / devicePixelRatio;
m[13] = Math.round(m[13] * devicePixelRatio) / devicePixelRatio;
var result = 'matrix3d(';
for (var i = 0; i < 15; i++) {
result += (m[i] < 0.000001 && m[i] > -0.000001) ? '0,' : m[i] + ',';
}
result += m[15] + ')';
return result;
}
/**
* Directly apply given FamousMatrix to the document element as the
* appropriate webkit CSS style.
*
* @method setMatrix
*
* @static
* @private
* @param {Element} element document element
* @param {FamousMatrix} matrix
*/
var _setMatrix;
if (usePrefix$1) {
_setMatrix = function(element, matrix) {
element.style.webkitTransform = _formatCSSTransform(matrix);
};
}
else {
_setMatrix = function(element, matrix) {
element.style.transform = _formatCSSTransform(matrix);
};
}
// format origin as CSS percentage string
function _formatCSSOrigin(origin) {
return (100 * origin[0]) + '% ' + (100 * origin[1]) + '%';
}
// Directly apply given origin coordinates to the document element as the
// appropriate webkit CSS style.
var _setOrigin = usePrefix$1 ? function(element, origin) {
element.style.webkitTransformOrigin = _formatCSSOrigin(origin);
} : function(element, origin) {
element.style.transformOrigin = _formatCSSOrigin(origin);
};
// Shrink given document element until it is effectively invisible.
var _setInvisible = usePrefix$1 ? function(element) {
element.style.webkitTransform = 'scale3d(0.0001,0.0001,0.0001)';
element.style.opacity = 0;
} : function(element) {
element.style.transform = 'scale3d(0.0001,0.0001,0.0001)';
element.style.opacity = 0;
};
function _xyNotEquals$1(a, b) {
return (a && b) ? (a[0] !== b[0] || a[1] !== b[1]) : a !== b;
}
/**
* Apply changes from this component to the corresponding document element.
* This includes changes to classes, styles, size, content, opacity, origin,
* and matrix transforms.
*
* @private
* @method commit
* @param {Context} context commit context
*/
ElementOutput.prototype.commit = function commit(context) {
var target = this._element;
if (!target) { return; }
var matrix = context.transform;
var opacity = context.opacity;
var origin = context.origin;
var size = context.size;
if (!matrix && this._matrix) {
this._matrix = null;
this._opacity = 0;
_setInvisible(target);
return;
}
if (_xyNotEquals$1(this._origin, origin)) { this._originDirty = true; }
if (Transform_1.notEquals(this._matrix, matrix)) { this._transformDirty = true; }
if (this._invisible) {
this._invisible = false;
this._element.style.display = '';
}
if (this._opacity !== opacity) {
this._opacity = opacity;
target.style.opacity = (opacity >= 1) ? '0.999999' : opacity;
}
if (this._transformDirty || this._originDirty || this._sizeDirty) {
if (this._sizeDirty) { this._sizeDirty = false; }
if (this._originDirty) {
if (origin) {
if (!this._origin) { this._origin = [0, 0]; }
this._origin[0] = origin[0];
this._origin[1] = origin[1];
}
else { this._origin = null; }
_setOrigin(target, this._origin);
this._originDirty = false;
}
if (!matrix) { matrix = Transform_1.identity; }
this._matrix = matrix;
var aaMatrix = this._size ? Transform_1.thenMove(matrix, [-this._size[0]*origin[0], -this._size[1]*origin[1], 0]) : matrix;
_setMatrix(target, aaMatrix);
this._transformDirty = false;
}
};
ElementOutput.prototype.cleanup = function cleanup() {
if (this._element) {
this._invisible = true;
this._element.style.display = 'none';
}
};
/**
* Place the document element that this component manages into the document.
*
* @private
* @method attach
* @param {Node} target document parent of this container
*/
ElementOutput.prototype.attach = function attach(target) {
this._element = target;
_addEventListeners.call(this, target);
};
/**
* Remove any contained document content associated with this surface
* from the actual document.
*
* @private
* @method detach
*/
ElementOutput.prototype.detach = function detach() {
var target = this._element;
if (target) {
_removeEventListeners.call(this, target);
if (this._invisible) {
this._invisible = false;
this._element.style.display = '';
}
}
this._element = null;
return target;
};
var ElementOutput_1 = ElementOutput;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* A base class for viewable content and event
* targets inside a Famo.us application, containing a renderable document
* fragment. Like an HTML div, it can accept internal markup,
* properties, classes, and handle events.
*
* @class Surface
* @constructor
*
* @param {Object} [options] default option overrides
* @param {Array.Number} [options.size] [width, height] in pixels
* @param {Array.string} [options.classes] CSS classes to set on target div
* @param {Array} [options.properties] string dictionary of CSS properties to set on target div
* @param {Array} [options.attributes] string dictionary of HTML attributes to set on target div
* @param {string} [options.content] inner (HTML) content of surface
*/
function Surface(options) {
ElementOutput_1.call(this);
this.options = {};
this.properties = {};
this.attributes = {};
this.content = '';
this.classList = [];
this.size = null;
this._classesDirty = true;
this._stylesDirty = true;
this._attributesDirty = true;
this._sizeDirty = true;
this._contentDirty = true;
this._trueSizeCheck = true;
this._dirtyClasses = [];
if (options) { this.setOptions(options); }
this._currentTarget = null;
}
Surface.prototype = Object.create(ElementOutput_1.prototype);
Surface.prototype.constructor = Surface;
Surface.prototype.elementType = 'div';
Surface.prototype.elementClass = 'famous-surface';
/**
* Set HTML attributes on this Surface. Note that this will cause
* dirtying and thus re-rendering, even if values do not change.
*
* @method setAttributes
* @param {Object} attributes property dictionary of "key" => "value"
*/
Surface.prototype.setAttributes = function setAttributes(attributes) {
for (var n in attributes) {
if (n === 'style') { throw new Error('Cannot set styles via "setAttributes" as it will break Famo.us. Use "setProperties" instead.'); }
this.attributes[n] = attributes[n];
}
this._attributesDirty = true;
};
/**
* Get HTML attributes on this Surface.
*
* @method getAttributes
*
* @return {Object} Dictionary of this Surface's attributes.
*/
Surface.prototype.getAttributes = function getAttributes() {
return this.attributes;
};
/**
* Set CSS-style properties on this Surface. Note that this will cause
* dirtying and thus re-rendering, even if values do not change.
*
* @method setProperties
* @chainable
* @param {Object} properties property dictionary of "key" => "value"
*/
Surface.prototype.setProperties = function setProperties(properties) {
for (var n in properties) {
this.properties[n] = properties[n];
}
this._stylesDirty = true;
return this;
};
/**
* Get CSS-style properties on this Surface.
*
* @method getProperties
*
* @return {Object} Dictionary of this Surface's properties.
*/
Surface.prototype.getProperties = function getProperties() {
return this.properties;
};
/**
* Add CSS-style class to the list of classes on this Surface. Note
* this will map directly to the HTML property of the actual
* corresponding rendered <div>.
*
* @method addClass
* @chainable
* @param {string} className name of class to add
*/
Surface.prototype.addClass = function addClass(className) {
if (this.classList.indexOf(className) < 0) {
this.classList.push(className);
this._classesDirty = true;
}
return this;
};
/**
* Remove CSS-style class from the list of classes on this Surface.
* Note this will map directly to the HTML property of the actual
* corresponding rendered <div>.
*
* @method removeClass
* @chainable
* @param {string} className name of class to remove
*/
Surface.prototype.removeClass = function removeClass(className) {
var i = this.classList.indexOf(className);
if (i >= 0) {
this._dirtyClasses.push(this.classList.splice(i, 1)[0]);
this._classesDirty = true;
}
return this;
};
/**
* Toggle CSS-style class from the list of classes on this Surface.
* Note this will map directly to the HTML property of the actual
* corresponding rendered <div>.
*
* @method toggleClass
* @param {string} className name of class to toggle
*/
Surface.prototype.toggleClass = function toggleClass(className) {
var i = this.classList.indexOf(className);
if (i >= 0) {
this.removeClass(className);
} else {
this.addClass(className);
}
return this;
};
/**
* Reset class list to provided dictionary.
* @method setClasses
* @chainable
* @param {Array.string} classList
*/
Surface.prototype.setClasses = function setClasses(classList) {
var i = 0;
var removal = [];
for (i = 0; i < this.classList.length; i++) {
if (classList.indexOf(this.classList[i]) < 0) { removal.push(this.classList[i]); }
}
for (i = 0; i < removal.length; i++) { this.removeClass(removal[i]); }
// duplicates are already checked by addClass()
for (i = 0; i < classList.length; i++) { this.addClass(classList[i]); }
return this;
};
/**
* Get array of CSS-style classes attached to this div.
*
* @method getClasslist
* @return {Array.string} array of class names
*/
Surface.prototype.getClassList = function getClassList() {
return this.classList;
};
/**
* Set or overwrite inner (HTML) content of this surface. Note that this
* causes a re-rendering if the content has changed.
*
* @method setContent
* @chainable
* @param {string|Document Fragment} content HTML content
*/
Surface.prototype.setContent = function setContent(content) {
if (this.content !== content) {
this.content = content;
this._contentDirty = true;
}
return this;
};
/**
* Return inner (HTML) content of this surface.
*
* @method getContent
*
* @return {string} inner (HTML) content
*/
Surface.prototype.getContent = function getContent() {
return this.content;
};
/**
* Set options for this surface
*
* @method setOptions
* @chainable
* @param {Object} [options] overrides for default options. See constructor.
*/
Surface.prototype.setOptions = function setOptions(options) {
if (options.size) { this.setSize(options.size); }
if (options.classes) { this.setClasses(options.classes); }
if (options.properties) { this.setProperties(options.properties); }
if (options.attributes) { this.setAttributes(options.attributes); }
if (options.content) { this.setContent(options.content); }
return this;
};
// Apply to document all changes from removeClass() since last setup().
function _cleanupClasses(target) {
for (var i = 0; i < this._dirtyClasses.length; i++) { target.classList.remove(this._dirtyClasses[i]); }
this._dirtyClasses = [];
}
// Apply values of all Famous-managed styles to the document element.
// These will be deployed to the document on call to #setup().
function _applyStyles(target) {
for (var n in this.properties) {
target.style[n] = this.properties[n];
}
}
// Clear all Famous-managed styles from the document element.
// These will be deployed to the document on call to #setup().
function _cleanupStyles(target) {
for (var n in this.properties) {
target.style[n] = '';
}
}
// Apply values of all Famous-managed attributes to the document element.
// These will be deployed to the document on call to #setup().
function _applyAttributes(target) {
for (var n in this.attributes) {
target.setAttribute(n, this.attributes[n]);
}
}
// Clear all Famous-managed attributes from the document element.
// These will be deployed to the document on call to #setup().
function _cleanupAttributes(target) {
for (var n in this.attributes) {
target.removeAttribute(n);
}
}
function _xyNotEquals(a, b) {
return (a && b) ? (a[0] !== b[0] || a[1] !== b[1]) : a !== b;
}
/**
* One-time setup for an element to be ready for commits to document.
*
* @private
* @method setup
*
* @param {ElementAllocator} allocator document element pool for this context
*/
Surface.prototype.setup = function setup(allocator) {
var target = allocator.allocate(this.elementType);
if (this.elementClass) {
if (this.elementClass instanceof Array) {
for (var i = 0; i < this.elementClass.length; i++) {
target.classList.add(this.elementClass[i]);
}
}
else {
target.classList.add(this.elementClass);
}
}
target.style.display = '';
this.attach(target);
this._opacity = null;
this._currentTarget = target;
this._stylesDirty = true;
this._classesDirty = true;
this._attributesDirty = true;
this._sizeDirty = true;
this._contentDirty = true;
this._originDirty = true;
this._transformDirty = true;
};
/**
* Apply changes from this component to the corresponding document element.
* This includes changes to classes, styles, size, content, opacity, origin,
* and matrix transforms.
*
* @private
* @method commit
* @param {Context} context commit context
*/
Surface.prototype.commit = function commit(context) {
if (!this._currentTarget) { this.setup(context.allocator); }
var target = this._currentTarget;
var size = context.size;
if (this._classesDirty) {
_cleanupClasses.call(this, target);
var classList = this.getClassList();
for (var i = 0; i < classList.length; i++) { target.classList.add(classList[i]); }
this._classesDirty = false;
this._trueSizeCheck = true;
}
if (this._stylesDirty) {
_applyStyles.call(this, target);
this._stylesDirty = false;
this._trueSizeCheck = true;
}
if (this._attributesDirty) {
_applyAttributes.call(this, target);
this._attributesDirty = false;
this._trueSizeCheck = true;
}
if (this.size) {
var origSize = context.size;
size = [this.size[0], this.size[1]];
if (size[0] === undefined) { size[0] = origSize[0]; }
if (size[1] === undefined) { size[1] = origSize[1]; }
if (size[0] === true || size[1] === true) {
if (size[0] === true){
if (this._trueSizeCheck || (this._size[0] === 0)) {
var width = target.offsetWidth;
if (this._size && this._size[0] !== width) {
this._size[0] = width;
this._sizeDirty = true;
}
size[0] = width;
} else {
if (this._size) { size[0] = this._size[0]; }
}
}
if (size[1] === true){
if (this._trueSizeCheck || (this._size[1] === 0)) {
var height = target.offsetHeight;
if (this._size && this._size[1] !== height) {
this._size[1] = height;
this._sizeDirty = true;
}
size[1] = height;
} else {
if (this._size) { size[1] = this._size[1]; }
}
}
this._trueSizeCheck = false;
}
}
if (_xyNotEquals(this._size, size)) {
if (!this._size) { this._size = [0, 0]; }
this._size[0] = size[0];
this._size[1] = size[1];
this._sizeDirty = true;
}
if (this._sizeDirty) {
if (this._size) {
target.style.width = (this.size && this.size[0] === true) ? '' : this._size[0] + 'px';
target.style.height = (this.size && this.size[1] === true) ? '' : this._size[1] + 'px';
}
this._eventOutput.emit('resize');
}
if (this._contentDirty) {
this.deploy(target);
this._eventOutput.emit('deploy');
this._contentDirty = false;
this._trueSizeCheck = true;
}
ElementOutput_1.prototype.commit.call(this, context);
};
/**
* Remove all Famous-relevant attributes from a document element.
* This is called by SurfaceManager's detach().
* This is in some sense the reverse of .deploy().
*
* @private
* @method cleanup
* @param {ElementAllocator} allocator
*/
Surface.prototype.cleanup = function cleanup(allocator) {
var i = 0;
var target = this._currentTarget;
this._eventOutput.emit('recall');
this.recall(target);
target.style.display = 'none';
target.style.opacity = '';
target.style.width = '';
target.style.height = '';
_cleanupStyles.call(this, target);
_cleanupAttributes.call(this, target);
var classList = this.getClassList();
_cleanupClasses.call(this, target);
for (i = 0; i < classList.length; i++) { target.classList.remove(classList[i]); }
if (this.elementClass) {
if (this.elementClass instanceof Array) {
for (i = 0; i < this.elementClass.length; i++) {
target.classList.remove(this.elementClass[i]);
}
}
else {
target.classList.remove(this.elementClass);
}
}
this.detach(target);
this._currentTarget = null;
allocator.deallocate(target);
};
/**
* Place the document element that this component manages into the document.
*
* @private
* @method deploy
* @param {Node} target document parent of this container
*/
Surface.prototype.deploy = function deploy(target) {
var content = this.getContent();
if (content instanceof Node) {
while (target.hasChildNodes()) { target.removeChild(target.firstChild); }
target.appendChild(content);
}
else { target.innerHTML = content; }
};
/**
* Remove any contained document content associated with this surface
* from the actual document.
*
* @private
* @method recall
*/
Surface.prototype.recall = function recall(target) {
var df = document.createDocumentFragment();
while (target.hasChildNodes()) { df.appendChild(target.firstChild); }
this.setContent(df);
};
/**
* Get the x and y dimensions of the surface.
*
* @method getSize
* @return {Array.Number} [x,y] size of surface
*/
Surface.prototype.getSize = function getSize() {
return this._size ? this._size : this.size;
};
/**
* Set x and y dimensions of the surface.
*
* @method setSize
* @chainable
* @param {Array.Number} size as [width, height]
*/
Surface.prototype.setSize = function setSize(size) {
this.size = size ? [size[0], size[1]] : null;
this._sizeDirty = true;
return this;
};
var Surface_1 = Surface;
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* Planes have the properties of [Molecules](#Molecule), plus they contain a
* [famous/src/core/Surface](#famous/src/core/Surface) so that they ultimately render
* onto the screen. A Surface's events are automatically piped to it's
* [famous/src/core/EventHandler](#famous/src/core/EventHandler), inherited from
* `Molecule`.
*
* @class Plane
* @extends Molecule
*/
var Plane = (function (Molecule$$1) {
function Plane(initialOptions) {
Molecule$$1.call(this, initialOptions);
this.surface = new Surface_1(this.options);
this.add(this.surface);
this.surface.pipe(this.options.handler);
}
if ( Molecule$$1 ) Plane.__proto__ = Molecule$$1;
Plane.prototype = Object.create( Molecule$$1 && Molecule$$1.prototype );
Plane.prototype.constructor = Plane;
/**
* Get the content of this Plane's [famous/src/core/Surface](#famous/src/core/Surface).
* See [famous/src/core/Surface.getContent](#famous/src/core/Surface.getContent).
*/
Plane.prototype.getContent = function getContent () {
const args = Array.prototype.splice.call(arguments, 0);
return this.surface.getContent.apply(this.surface, args);
};
/**
* Set the content of this Plane's [famous/src/core/Surface](#famous/src/core/Surface).
* See [famous/src/core/Surface.setContent](#famous/src/core/Surface.setContent).
*/
Plane.prototype.setContent = function setContent () {
const args = Array.prototype.splice.call(arguments, 0);
return this.surface.setContent.apply(this.surface, args);
};
return Plane;
}(Molecule));
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* A scenegraph tree who's two leaf nodes are [Plane](#Plane) instances facing
* opposite directions. For the purposes of these docs, in a brand new app with
* only a single `DoubleSidedPlane` added to the context, and having no
* rotation, "plane1" faces you and "plane2" faces away.
*
* @class DoubleSidedPlane
* @extends Molecule
*/
var DoubleSidedPlane = (function (Molecule$$1) {
function DoubleSidedPlane(initialOptions) {
Molecule$$1.call(this, initialOptions);
this.children = [];
this.plane1 = new Plane(this.options);
this.plane1.transform.set(Transform_1.rotate(0,0,0));
this.setOptions({properties: {background: 'orange'}});
this.plane2 = new Plane(this.options);
this.plane2.transform.set(Transform_1.rotate(0,Math.PI,0));
this.children.push(this.plane1);
this.children.push(this.plane2);
this.add(this.plane2);
this.add(this.plane1);
this.plane1.pipe(this.options.handler);
this.plane2.pipe(this.options.handler);
}
if ( Molecule$$1 ) DoubleSidedPlane.__proto__ = Molecule$$1;
DoubleSidedPlane.prototype = Object.create( Molecule$$1 && Molecule$$1.prototype );
DoubleSidedPlane.prototype.constructor = DoubleSidedPlane;
/**
* Get the content of the [famous/src/core/Surface](#famous/src/core/Surface) of each [Plane](#Plane).
*
* @returns {Array} An array containing two items, the content of each
* `Plane`. The first item is from "plane1".
*/
DoubleSidedPlane.prototype.getContent = function getContent () {
return [this.plane1.getContent(), this.plane2.getContent()];
};
/**
* Set the content of both [Plane](#Plane) instances.
*
* @param {Array} content An array of content, one item per `Plane`. The
* first item is for "plane1".
*/
DoubleSidedPlane.prototype.setContent = function setContent (content) {
this.plane1.setContent(content[0]);
this.plane2.setContent(content[1]);
};
return DoubleSidedPlane;
}(Molecule));
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* A calendar widget for selecting a date (WIP).
*
* @class Calendar
* @extends Molecule
*/
var Calendar = (function (Molecule$$1) {
function Calendar(calendarSize, transition) {
Molecule$$1.call(this, {size: calendarSize});
this.transition = transition;
this.flipSide = 0; // 0 means the initial front faces are showing, 1 means the initial back faces are showing.
this.columnsRows = [7,6];
this.planes = [];
this._initializeTransitions();
this._createGrid();
setTimeout( function() {
this.transitions[this.transition]();
setInterval(this.transitions[this.transition], 2000);
}.bind(this) , 800);
}
if ( Molecule$$1 ) Calendar.__proto__ = Molecule$$1;
Calendar.prototype = Object.create( Molecule$$1 && Molecule$$1.prototype );
Calendar.prototype.constructor = Calendar;
/**
* Creates the grid used for the layout of the day cells.
*
* @private
*/
Calendar.prototype._createGrid = function _createGrid () {
const grid = new Grid(this.columnsRows[0], this.columnsRows[1], this.options.size);
forLength(this.columnsRows[0]*this.columnsRows[1], function(i) {
const plane = new DoubleSidedPlane({
properties: {
background: 'teal',
}
});
this.planes.push(plane);
}.bind(this));
grid.setChildren(this.planes);
this.add(grid);
};
/**
* Set up `this.transitions`, containing the available month-to-month
* transitions.
*
* @private
*/
Calendar.prototype._initializeTransitions = function _initializeTransitions () {
this.transitions = {
flipDiagonal: function() {
this.flipSide = +!this.flipSide;
// determine which dimension of the grid is shorter and which is longer.
let shortest = 0;
let longest;
this.columnsRows.forEach(function(item, index) {
if (item < this.columnsRows[shortest])
{ shortest = index; }
}.bind(this));
longest = +!shortest;
// for each diagonal of the grid, flip those cells.
forLength(this.columnsRows[0]+this.columnsRows[1]-1, function(column) {
forLength(this.columnsRows[shortest], function(row) {
if (column-row >= 0 && column-row < this.columnsRows[longest]) {
const plane = this.planes[column-row + this.columnsRows[longest]*row];
flipOne(plane, column);
}
}.bind(this));
}.bind(this));
function flipOne(item, column) {
if (typeof item.__targetRotation == 'undefined') {
item.__targetRotation = new Transitionable_1(0);
}
const rotation = new Transitionable_1(item.__targetRotation.get());
item.__targetRotation.set(item.__targetRotation.get()+Math.PI);
//item.get().transformFrom(function() {
//return Transform.rotateY(rotation.get());
//});
item.children[0].get().transformFrom(function() {
return Transform_1.rotateY(rotation.get());
});
item.children[1].get().transformFrom(function() {
return Transform_1.rotateY(rotation.get()+Math.PI);
});
setTimeout(function() {
rotation.set(item.__targetRotation.get(), { duration: 2000, curve: Easing_1.outExpo });
}, 0+50*column);
}
}.bind(this)
};
};
return Calendar;
}(Molecule));
var utils$1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.clone = clone;
exports.isEmptyObject = isEmptyObject;
exports.toCSS = toCSS;
var stringify = JSON.stringify;
var parse = JSON.parse;
/**
* Deeply clone object using serialization.
* Expects object to be plain and without cyclic dependencies.
*
* http://jsperf.com/lodash-deepclone-vs-jquery-extend-deep/6
*
* @type {Object} obj
* @return {Object}
*/
function clone(obj) {
return parse(stringify(obj));
}
/**
* Determine whether an object is empty or not.
* More performant than a `Object.keys(obj).length > 0`
*
* @type {Object} obj
* @return {Boolean}
*/
function isEmptyObject(obj) {
for (var key in obj) {
return false;
} // eslint-disable-line no-unused-vars
return true;
}
/**
* Simple very fast UID generation based on a global counter.
*/
var uid = exports.uid = function () {
var globalReference = typeof window == 'undefined' ? commonjsGlobal : window;
var namespace = '__JSS_VERSION_COUNTER__';
if (globalReference[namespace] == null) { globalReference[namespace] = 0; }
// In case we have more than one jss version.
var versionCounter = globalReference[namespace]++;
var ruleCounter = 0;
/**
* Returns a uid.
* Ensures uniqueness if more than 1 jss version is used.
*
* @api public
* @return {String}
*/
function get() {
return 'jss-' + versionCounter + '-' + ruleCounter++;
}
/**
* Resets the counter.
*
* @api public
*/
function reset() {
ruleCounter = 0;
}
return { get: get, reset: reset };
}();
/**
* Indent a string.
*
* http://jsperf.com/array-join-vs-for
*
* @param {Number} level
* @param {String} str
* @return {String}
*/
function indent(level, str) {
var indentStr = '';
for (var index = 0; index < level; index++) {
indentStr += ' ';
}return indentStr + str;
}
/**
* Converts a Rule to CSS string.
*
* Options:
* - `selector` use `false` to get a rule without selector
* - `indentationLevel` level of indentation
*
* @param {String} selector
* @param {Object} style
* @param {Object} options
* @return {String}
*/
function toCSS(selector, style) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var indentationLevel = options.indentationLevel || 0;
var str = '';
if (options.selector !== false) {
str += indent(indentationLevel, selector + ' {');
indentationLevel++;
}
for (var prop in style) {
var value = style[prop];
// We want to generate multiple style with identical property names.
if (Array.isArray(value)) {
for (var index = 0; index < value.length; index++) {
str += '\n' + indent(indentationLevel, prop + ': ' + value[index] + ';');
}
} else { str += '\n' + indent(indentationLevel, prop + ': ' + value + ';'); }
}
if (options.selector !== false) { str += '\n' + indent(--indentationLevel, '}'); }
return str;
}
/**
* Get class names from a selector.
*
* @param {String} selector
* @return {String}
*/
var findClassNames = exports.findClassNames = function () {
var dotsRegExp = /[.]/g;
var classesRegExp = /[.][^ ,]+/g;
return function (selector) {
var classes = selector.match(classesRegExp);
if (!classes) { return ''; }
return classes.join(' ').replace(dotsRegExp, '');
};
}();
});
var Rule_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Regular rules.
*
* @api public
*/
var Rule = function () {
function Rule(selector, style, options) {
_classCallCheck(this, Rule);
this.id = utils$1.uid.get();
this.type = 'regular';
this.options = options;
this.selectorText = selector || '';
this.className = options.className || '';
this.originalStyle = style;
// We expect style to be plain object.
this.style = (0, utils$1.clone)(style);
if (options.named) {
this.name = selector;
if (!this.className) {
this.className = this.name ? this.name + '--' + this.id : this.id;
}
this.selectorText = '.' + this.className;
}
}
/**
* Set selector string.
* Attenition: use this with caution. Most browser didn't implement selector
* text setter, so this will result in rerendering of entire style sheet.
*
* @param {String} selector
* @api public
*/
_createClass(Rule, [{
key: 'prop',
/**
* Get or set a style property.
*
* @param {String} name
* @param {String|Number} [value]
* @return {Rule|String|Number}
* @api public
*/
value: function prop(name, value) {
var style = this.options.Renderer.style;
// Its a setter.
if (value != null) {
this.style[name] = value;
// Only defined if option linked is true.
if (this.renderable) { style(this.renderable, name, value); }
return this;
}
// Its a getter, read the value from the DOM if its not cached.
if (this.renderable && this.style[name] == null) {
// Cache the value after we have got it from the DOM once.
this.style[name] = style(this.renderable, name);
}
return this.style[name];
}
/**
* Apply rule to an element inline.
*
* @param {Element} renderable
* @return {Rule}
* @api public
*/
}, {
key: 'applyTo',
value: function applyTo(renderable) {
for (var prop in this.style) {
var value = this.style[prop];
var style = this.options.Renderer.style;
if (Array.isArray(value)) {
for (var index = 0; index < value.length; index++) {
style(renderable, prop, value[index]);
}
} else { style(renderable, prop, value); }
}
return this;
}
/**
* Returns JSON representation of the rule.
* Array of values is not supported.
*
* @return {Object}
* @api public
*/
}, {
key: 'toJSON',
value: function toJSON() {
var style = Object.create(null);
for (var prop in this.style) {
if (_typeof(this.style[prop]) != 'object') {
style[prop] = this.style[prop];
}
}
return style;
}
/**
* Generates a CSS string.
*
* @see toCSS
* @api public
*/
}, {
key: 'toString',
value: function toString(options) {
return (0, utils$1.toCSS)(this.selector, this.style, options);
}
}, {
key: 'selector',
set: function set() {
var selector = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
var _options = this.options;
var Renderer = _options.Renderer;
var sheet = _options.sheet;
// After we modify selector, ref by old selector needs to be removed.
if (sheet) { sheet.unregisterRule(this); }
this.selectorText = selector;
this.className = (0, utils$1.findClassNames)(selector);
if (!this.renderable) {
// Register the rule with new selector.
if (sheet) { sheet.registerRule(this); }
return;
}
var changed = Renderer.setSelector(this.renderable, selector);
if (changed) {
sheet.registerRule(this);
return;
}
// If selector setter is not implemented, rerender the sheet.
// We need to delete renderable from the rule, because when sheet.deploy()
// calls rule.toString, it will get the old selector.
delete this.renderable;
sheet.registerRule(this).deploy().link();
}
/**
* Get selector string.
*
* @return {String}
* @api public
*/
,
get: function get() {
if (this.renderable) {
return this.options.Renderer.getSelector(this.renderable);
}
return this.selectorText;
}
}]);
return Rule;
}();
exports.default = Rule;
});
var SimpleRule_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Rule like @charset, @import, @namespace.
*
* @api public
*/
var SimpleRule = function () {
function SimpleRule(name, value, options) {
_classCallCheck(this, SimpleRule);
this.id = utils$1.uid.get();
this.type = 'simple';
this.name = name;
this.value = value;
this.options = options;
}
/**
* Generates a CSS string.
*
* @return {String}
* @api public
*/
_createClass(SimpleRule, [{
key: 'toString',
value: function toString() {
if (Array.isArray(this.value)) {
var str = '';
for (var index = 0; index < this.value.length; index++) {
str += this.name + ' ' + this.value[index] + ';';
if (this.value[index + 1]) { str += '\n'; }
}
return str;
}
return this.name + ' ' + this.value + ';';
}
}]);
return SimpleRule;
}();
exports.default = SimpleRule;
});
var KeyframeRule_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Keyframe rule.
*
* @api private
*/
var KeyframeRule = function () {
function KeyframeRule(selector, frames, options) {
_classCallCheck(this, KeyframeRule);
this.id = utils$1.uid.get();
this.type = 'keyframe';
this.selector = selector;
this.options = options;
this.frames = this.formatFrames(frames);
}
/**
* Creates formatted frames where every frame value is a rule instance.
*
* @api private
*/
_createClass(KeyframeRule, [{
key: 'formatFrames',
value: function formatFrames(frames) {
var newFrames = Object.create(null);
for (var name in frames) {
var options = _extends({}, this.options, { named: false, parent: this });
newFrames[name] = this.options.jss.createRule(name, frames[name], options);
}
return newFrames;
}
/**
* Generates a CSS string.
*
* @return {String}
* @api private
*/
}, {
key: 'toString',
value: function toString() {
var str = this.selector + ' {\n';
var options = { indentationLevel: 1 };
for (var name in this.frames) {
str += this.frames[name].toString(options) + '\n';
}
str += '}';
return str;
}
}]);
return KeyframeRule;
}();
exports.default = KeyframeRule;
});
var ConditionalRule_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Conditional rule for @media, @supports
*
* @api public
*/
var ConditionalRule = function () {
function ConditionalRule(selector, styles, options) {
_classCallCheck(this, ConditionalRule);
this.id = utils$1.uid.get();
this.type = 'conditional';
this.selector = selector;
this.options = options;
this.rules = Object.create(null);
for (var name in styles) {
this.createRule(name, styles[name]);
}
}
/**
* A conditional rule always contains child rules.
*
* @param {Object} styles
* @return {Array} rules
* @api public
*/
_createClass(ConditionalRule, [{
key: 'createRule',
value: function createRule(name, style, options) {
var newOptions = _extends({}, this.options, { parent: this });
var _newOptions = newOptions;
var sheet = _newOptions.sheet;
var jss = _newOptions.jss;
// We have already a rule in the current style sheet with this name,
// This new rule is supposed to overwrite the first one, for this we need
// to ensure it will have the same className/selector.
var existingRule = sheet && sheet.getRule(name);
var className = existingRule ? existingRule.className : null;
if (className || options) {
newOptions = _extends({}, newOptions, { className: className }, options);
}
var rule = (sheet || jss).createRule(name, style, newOptions);
this.rules[name] = rule;
return rule;
}
/**
* Generates a CSS string.
*
* @return {String}
* @api public
*/
}, {
key: 'toString',
value: function toString() {
var str = this.selector + ' {\n';
for (var name in this.rules) {
var rule = this.rules[name];
if (rule.style && (0, utils$1.isEmptyObject)(rule.style)) {
continue;
}
var ruleStr = rule.toString({ indentationLevel: 1 });
str += ruleStr + '\n';
}
str += '}';
return str;
}
}]);
return ConditionalRule;
}();
exports.default = ConditionalRule;
});
var FontFaceRule = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Font-face rules.
*
* @api public
*/
var Rule = function () {
function Rule(selector, style, options) {
_classCallCheck(this, Rule);
this.id = utils$1.uid.get();
this.type = 'font-face';
this.options = options;
this.selector = selector;
this.style = style;
}
/**
* Generates a CSS string.
*
* @see toCSS
* @api public
*/
_createClass(Rule, [{
key: 'toString',
value: function toString(options) {
if (Array.isArray(this.style)) {
var str = '';
for (var index = 0; index < this.style.length; index++) {
str += (0, utils$1.toCSS)(this.selector, this.style[index], options);
if (this.style[index + 1]) { str += '\n'; }
}
return str;
}
return (0, utils$1.toCSS)(this.selector, this.style, options);
}
}]);
return Rule;
}();
exports.default = Rule;
});
var createRule_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createRule;
var _Rule2 = _interopRequireDefault(Rule_1);
var _SimpleRule2 = _interopRequireDefault(SimpleRule_1);
var _KeyframeRule2 = _interopRequireDefault(KeyframeRule_1);
var _ConditionalRule2 = _interopRequireDefault(ConditionalRule_1);
var _FontFaceRule2 = _interopRequireDefault(FontFaceRule);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Map of at rules to corresponding implementation class.
*
* @type {Object}
*/
var atRuleClassMap = {
'@charset': _SimpleRule2.default,
'@import': _SimpleRule2.default,
'@namespace': _SimpleRule2.default,
'@keyframes': _KeyframeRule2.default,
'@media': _ConditionalRule2.default,
'@supports': _ConditionalRule2.default,
'@font-face': _FontFaceRule2.default
};
var atRuleNameRegExp = /^@[^ ]+/;
/**
* Create rule factory.
*
* @param {Object} [selector] if you don't pass selector - it will be generated
* @param {Object} [style] declarations block
* @param {Object} [options] rule options
* @return {Object} rule
* @api private
*/
function createRule(selector) {
var style = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
// Is an at-rule.
if (selector && selector[0] === '@') {
var name = atRuleNameRegExp.exec(selector)[0];
var AtRule = atRuleClassMap[name];
return new AtRule(selector, style, options);
}
if (options.named == null) { options.named = true; }
return new _Rule2.default(selector, style, options);
}
});
var DomRenderer_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* DOM rendering backend for StyleSheet.
*
* @api private
*/
var DomRenderer = function () {
_createClass(DomRenderer, null, [{
key: 'style',
value: function style(element, name, value) {
try {
if (value == null) { return element.style[name]; }
element.style[name] = value;
} catch (err) {
// IE8 may throw if property is unknown.
return false;
}
return true;
}
}, {
key: 'setSelector',
value: function setSelector(cssRule, selector) {
cssRule.selectorText = selector;
// Return false if setter was not successful.
// Currently works in chrome only.
return cssRule.selectorText === selector;
}
}, {
key: 'getSelector',
value: function getSelector(cssRule) {
return cssRule.selectorText;
}
}]);
function DomRenderer(options) {
_classCallCheck(this, DomRenderer);
this.head = document.head || document.getElementsByTagName('head')[0];
this.element = options.element || document.createElement('style');
// IE8 will not have `styleSheet` prop without `type and `styleSheet.cssText`
// is the only way to render on IE8.
this.element.type = 'text/css';
if (options.media) { this.element.setAttribute('media', options.media); }
if (options.meta) { this.element.setAttribute('data-meta', options.meta); }
}
/**
* Insert style element into render tree.
*
* @api private
*/
_createClass(DomRenderer, [{
key: 'attach',
value: function attach() {
if (this.element.parendNode) { return; }
this.head.appendChild(this.element);
}
/**
* Remove style element from render tree.
*
* @api private
*/
}, {
key: 'detach',
value: function detach() {
this.element.parentNode.removeChild(this.element);
}
/**
* Inject CSS string into element.
*
* @param {String} cssStr
* @api private
*/
}, {
key: 'deploy',
value: function deploy(sheet) {
var css = '\n' + sheet.toString() + '\n';
if ('sheet' in this.element) { this.element.innerHTML = css; }
// On IE8 the only way to render is `styleSheet.cssText`.
else if ('styleSheet' in this.element) { this.element.styleSheet.cssText = css; }
}
/**
* Insert a rule into element.
*
* @param {Rule} rule
* @return {CSSStyleRule}
* @api private
*/
}, {
key: 'insertRule',
value: function insertRule(rule) {
// IE8 has only `styleSheet` and `styleSheet.rules`
var sheet = this.element.sheet || this.element.styleSheet;
var cssRules = sheet.cssRules || sheet.rules;
var nextIndex = cssRules.length;
if (sheet.insertRule) { sheet.insertRule(rule.toString(), nextIndex); }else { sheet.addRule(rule.selector, rule.toString({ selector: false }), nextIndex); }
return cssRules[nextIndex];
}
/**
* Get all rules elements.
*
* @return {Object} rules map, where key is selector, CSSStyleRule is value.
* @api private
*/
}, {
key: 'getRules',
value: function getRules() {
// IE8 has only `styleSheet` and `styleSheet.rules`
var sheet = this.element.sheet || this.element.styleSheet;
var cssRules = sheet.rules || sheet.cssRules;
var rules = Object.create(null);
for (var index = 0; index < cssRules.length; index++) {
var cssRule = cssRules[index];
rules[cssRule.selectorText] = cssRule;
}
return rules;
}
}]);
return DomRenderer;
}();
exports.default = DomRenderer;
});
var VirtualRenderer_1 = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Rendering backend to do nothing in nodejs.
*/
var VirtualRenderer = function () {
function VirtualRenderer() {
_classCallCheck(this, VirtualRenderer);
}
_createClass(VirtualRenderer, [{
key: "attach",
value: function attach() {}
}, {
key: "detach",
value: function detach() {}
}, {
key: "deploy",
value: function deploy() {}
}, {
key: "insertRule",
value: function insertRule() {}
}, {
key: "getRules",
value: function getRules() {
return {};
}
}], [{
key: "style",
value: function style() {}
}, {
key: "setSelector",
value: function setSelector() {}
}, {
key: "getSelector",
value: function getSelector() {}
}]);
return VirtualRenderer;
}();
exports.default = VirtualRenderer;
});
var findRenderer_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = findRenderer;
var _DomRenderer2 = _interopRequireDefault(DomRenderer_1);
var _VirtualRenderer2 = _interopRequireDefault(VirtualRenderer_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Find proper renderer.
* Option `virtual` is used to force use of VirtualRenderer even if DOM is
* detected, used for testing only.
*
* @param {Object} options
* @return {Renderer}
* @api private
*/
function findRenderer() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
if (options.Renderer) { return options.Renderer; }
return options.virtual || typeof document == 'undefined' ? _VirtualRenderer2.default : _DomRenderer2.default;
}
});
var StyleSheet_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
var _createRule3 = _interopRequireDefault(createRule_1);
var _findRenderer2 = _interopRequireDefault(findRenderer_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* StyleSheet model.
*
* Options:
*
* - `media` media query - attribute of style element.
* - `meta` meta information about this style - attribute of style element, for e.g. you could pass
* component name for easier debugging.
* - `named` true by default - keys are names, selectors will be generated, if false - keys are
* global selectors.
* - `link` link jss `Rule` instances with DOM `CSSRule` instances so that styles, can be modified
* dynamically, false by default because it has some performance cost.
* - `element` style element, will create one by default
*
* @param {Object} [rules] object with selectors and declarations
* @param {Object} [options]
* @api public
*/
var StyleSheet = function () {
function StyleSheet(rules, options) {
_classCallCheck(this, StyleSheet);
this.options = _extends({}, options);
if (this.options.named == null) { this.options.named = true; }
this.rules = Object.create(null);
this.classes = Object.create(null);
this.attached = false;
this.deployed = false;
this.linked = false;
var Renderer = (0, _findRenderer2.default)(this.options);
this.options.Renderer = Renderer;
this.renderer = new Renderer(this.options);
for (var name in rules) {
this.createRule(name, rules[name]);
}
}
/**
* Attach renderable to the render tree.
*
* @api public
* @return {StyleSheet}
*/
_createClass(StyleSheet, [{
key: 'attach',
value: function attach() {
if (this.attached) { return this; }
if (!this.deployed) { this.deploy(); }
this.renderer.attach();
if (!this.linked && this.options.link) { this.link(); }
this.attached = true;
return this;
}
/**
* Remove renderable from render tree.
*
* @return {StyleSheet}
* @api public
*/
}, {
key: 'detach',
value: function detach() {
if (!this.attached) { return this; }
this.renderer.detach();
this.attached = false;
return this;
}
/**
* Add a rule to the current stylesheet. Will insert a rule also after the stylesheet
* has been rendered first time.
*
* @param {Object} [name] can be selector or name if ´options.named is true
* @param {Object} style property/value hash
* @return {Rule}
* @api public
*/
}, {
key: 'addRule',
value: function addRule(name, style) {
var rule = this.createRule(name, style);
// Don't insert rule directly if there is no stringified version yet.
// It will be inserted all together when .attach is called.
if (this.deployed) {
var renderable = this.renderer.insertRule(rule);
if (this.options.link) { rule.renderable = renderable; }
}
return rule;
}
/**
* Create rules, will render also after stylesheet was rendered the first time.
*
* @param {Object} rules name:style hash.
* @return {Array} array of added rules
* @api public
*/
}, {
key: 'addRules',
value: function addRules(rules) {
var added = [];
for (var name in rules) {
added.push(this.addRule(name, rules[name]));
}
return added;
}
/**
* Get a rule.
*
* @param {String} name can be selector or name if `named` option is true.
* @return {Rule}
* @api public
*/
}, {
key: 'getRule',
value: function getRule(name) {
return this.rules[name];
}
/**
* Convert rules to a CSS string.
*
* @param {Object} options
* @return {String}
* @api public
*/
}, {
key: 'toString',
value: function toString(options) {
var rules = this.rules;
var stringified = Object.create(null);
var str = '';
for (var name in rules) {
var rule = rules[name];
// We have the same rule referenced twice if using named rules.
// By name and by selector.
if (stringified[rule.id]) {
continue;
}
if (rule.style && (0, utils$1.isEmptyObject)(rule.style)) {
continue;
}
if (rule.rules && (0, utils$1.isEmptyObject)(rule.rules)) {
continue;
}
if (str) { str += '\n'; }
str += rule.toString(options);
stringified[rule.id] = true;
}
return str;
}
/**
* Create a rule, will not render after stylesheet was rendered the first time.
* Will link the rule in `this.rules`.
*
* @see createRule
* @api private
*/
}, {
key: 'createRule',
value: function createRule(name, style, options) {
options = _extends({}, options, {
sheet: this,
jss: this.options.jss,
Renderer: this.options.Renderer
});
// Scope options overwrite instance options.
if (options.named == null) { options.named = this.options.named; }
var rule = (0, _createRule3.default)(name, style, options);
this.registerRule(rule);
options.jss.plugins.run(rule);
return rule;
}
/**
* Register a rule in `sheet.rules` and `sheet.classes` maps.
*
* @param {Rule} rule
* @api public
*/
}, {
key: 'registerRule',
value: function registerRule(rule) {
// Children of container rules should not be registered.
if (rule.options.parent) {
// We need to register child rules of conditionals in classes, otherwise
// user can't access generated class name if it doesn't overrides
// a regular rule.
if (rule.name && rule.className) {
this.classes[rule.name] = rule.className;
}
return this;
}
if (rule.name) {
this.rules[rule.name] = rule;
if (rule.className) { this.classes[rule.name] = rule.className; }
}
if (rule.selector) {
this.rules[rule.selector] = rule;
}
return this;
}
/**
* Unregister a rule.
*
* @param {Rule} rule
* @api public
*/
}, {
key: 'unregisterRule',
value: function unregisterRule(rule) {
// Children of a conditional rule are not registered.
if (!rule.options.parent) {
delete this.rules[rule.name];
delete this.rules[rule.selector];
}
delete this.classes[rule.name];
return this;
}
/**
* Deploy pure CSS string to a renderable.
*
* @return {StyleSheet}
* @api private
*/
}, {
key: 'deploy',
value: function deploy() {
this.renderer.deploy(this);
this.deployed = true;
return this;
}
/**
* Link renderable CSS rules with their corresponding models.
*
* @return {StyleSheet}
* @api private
*/
}, {
key: 'link',
value: function link() {
var renderables = this.renderer.getRules();
for (var selector in renderables) {
var rule = this.rules[selector];
if (rule) { rule.renderable = renderables[selector]; }
}
this.linked = true;
return this;
}
}]);
return StyleSheet;
}();
exports.default = StyleSheet;
});
var PluginsRegistry_1 = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Register a plugin, run a plugin.
*
* @api public
*/
var PluginsRegistry = function () {
function PluginsRegistry() {
_classCallCheck(this, PluginsRegistry);
this.registry = [];
}
/**
* Register plugin. Passed function will be invoked with a rule instance.
*
* @param {Function} fn
* @api public
*/
_createClass(PluginsRegistry, [{
key: "use",
value: function use(fn) {
this.registry.push(fn);
}
/**
* Execute all registered plugins.
*
* @param {Rule} rule
* @api private
*/
}, {
key: "run",
value: function run(rule) {
for (var index = 0; index < this.registry.length; index++) {
this.registry[index](rule);
}
}
}]);
return PluginsRegistry;
}();
exports.default = PluginsRegistry;
});
var SheetsRegistry_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Sheets registry to access them all at one place.
*
* @api public
*/
var SheetsRegistry = function () {
function SheetsRegistry() {
_classCallCheck(this, SheetsRegistry);
this.registry = [];
}
/**
* Register a style sheet.
*
* @param {StyleSheet} sheet
* @api public
*/
_createClass(SheetsRegistry, [{
key: 'add',
value: function add(sheet) {
this.registry.push(sheet);
}
/**
* Returns CSS string with all Style Sheets.
*
* @param {StyleSheet} sheet
* @api public
*/
}, {
key: 'toString',
value: function toString(options) {
return this.registry.map(function (sheet) {
return sheet.toString(options);
}).join('\n');
}
}]);
return SheetsRegistry;
}();
exports.default = SheetsRegistry;
});
var Jss_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true; } Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) { defineProperties(Constructor.prototype, protoProps); } if (staticProps) { defineProperties(Constructor, staticProps); } return Constructor; }; }();
var _StyleSheet2 = _interopRequireDefault(StyleSheet_1);
var _PluginsRegistry2 = _interopRequireDefault(PluginsRegistry_1);
var _SheetsRegistry2 = _interopRequireDefault(SheetsRegistry_1);
var _createRule3 = _interopRequireDefault(createRule_1);
var _findRenderer2 = _interopRequireDefault(findRenderer_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Main Jss class.
*
* @api public
*/
var Jss = function () {
function Jss() {
_classCallCheck(this, Jss);
this.sheets = new _SheetsRegistry2.default();
this.plugins = new _PluginsRegistry2.default();
this.uid = utils$1.uid;
this.version = '3.11.1';
}
/**
* Creates a new instance of Jss.
*
* @see Jss
* @api public
*/
_createClass(Jss, [{
key: 'create',
value: function create() {
return new Jss();
}
/**
* Create a stylesheet.
*
* @see StyleSheet
* @api public
*/
}, {
key: 'createStyleSheet',
value: function createStyleSheet(rules, options) {
var sheet = new _StyleSheet2.default(rules, _extends({}, options, { jss: this }));
this.sheets.add(sheet);
return sheet;
}
/**
* Create a rule.
*
* @see createRule
* @api public
*/
}, {
key: 'createRule',
value: function createRule(selector, style, options) {
// Enable rule without selector.
if ((typeof selector === 'undefined' ? 'undefined' : _typeof(selector)) == 'object') {
options = style;
style = selector;
selector = null;
}
var rule = (0, _createRule3.default)(selector, style, _extends({}, options, {
jss: this,
Renderer: (0, _findRenderer2.default)(options)
}));
this.plugins.run(rule);
return rule;
}
/**
* Register plugin. Passed function will be invoked with a rule instance.
*
* @param {Function} plugins
* @api public
*/
}, {
key: 'use',
value: function use() {
var _this = this;
for (var _len = arguments.length, plugins = Array(_len), _key = 0; _key < _len; _key++) {
plugins[_key] = arguments[_key];
}
plugins.forEach(function (plugin) {
return _this.plugins.use(plugin);
});
return this;
}
}]);
return Jss;
}();
exports.default = Jss;
});
var index = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Rule = exports.StyleSheet = exports.Jss = undefined;
var _Jss2 = _interopRequireDefault(Jss_1);
var _StyleSheet2 = _interopRequireDefault(StyleSheet_1);
var _Rule2 = _interopRequireDefault(Rule_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var jss = new _Jss2.default();
// Hotfix for babel 5 migration, will be removed in version 4.0.0
/**
* StyleSheets written in javascript.
*
* @copyright Oleg Slobodskoi 2014-2016
* @website https://github.com/jsstyles/jss
* @license MIT
*/
module.exports = exports = jss;
// For testing only.
exports.Jss = _Jss2.default;
exports.StyleSheet = _StyleSheet2.default;
exports.Rule = _Rule2.default;
exports.default = jss;
});
var Jss = unwrapExports(index);
var index$1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.default = jssNested;
var regExp = /&/g;
/**
* Convert nested rules to separate, remove them from original styles.
*
* @param {Rule} rule
* @api public
*/
function jssNested() {
return function (rule) {
if (rule.type !== 'regular') { return; }
var _rule$options = rule.options;
var sheet = _rule$options.sheet;
var jss = _rule$options.jss;
var parent = _rule$options.parent;
var container = sheet || jss;
var options = void 0;
if (parent && parent.type === 'conditional') {
container = parent;
}
for (var prop in rule.style) {
if (prop[0] === '&') {
if (!options) { options = _extends({}, rule.options, { named: false }); }
var name = prop.replace(regExp, rule.selector);
container.createRule(name, rule.style[prop], options);
delete rule.style[prop];
}
}
};
}
});
var jssNested = unwrapExports(index$1);
var index$2 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = jssExtend;
/**
* Handle `extend` property.
*
* @param {Rule} rule
* @api public
*/
function jssExtend() {
function extend(rule, newStyle, style) {
if (typeof style.extend == 'string') {
if (rule.options && rule.options.sheet) {
var refRule = rule.options.sheet.getRule(style.extend);
if (refRule) { extend(rule, newStyle, refRule.originalStyle); }
}
} else if (Array.isArray(style.extend)) {
for (var index = 0; index < style.extend.length; index++) {
extend(rule, newStyle, style.extend[index]);
}
} else {
for (var prop in style.extend) {
if (prop === 'extend') { extend(rule, newStyle, style.extend.extend); }else { newStyle[prop] = style.extend[prop]; }
}
}
// Copy base style.
for (var _prop in style) {
if (_prop !== 'extend') { newStyle[_prop] = style[_prop]; }
}
return newStyle;
}
return function (rule) {
if (!rule.style || !rule.style.extend) { return; }
rule.style = extend(rule, {}, rule.style);
};
}
});
var jssExtend = unwrapExports(index$2);
var index$3 = createCommonjsModule(function (module, exports) {
// Don't automatically add 'px' to these possibly-unitless properties.
// Borrowed from jquery.
'use strict';
exports.__esModule = true;
exports['default'] = jssPx;
var cssNumber = {
'animation-iteration-count': true,
'box-ordinal-group': true,
'column-count': true,
'fill-opacity': true,
'flex': true,
'flex-grow': true,
'flex-order': true,
'flex-shrink': true,
'font-weight': true,
'line-height': true,
'opacity': true,
'order': true,
'orphans': true,
'stop-opacity': true,
'tab-size': 1,
'widows': true,
'z-index': true,
'zoom': true
};
/**
* Add px to numeric values.
*
* @param {Rule} rule
* @api public
*/
function jssPx() {
return function (rule) {
var style = rule.style;
if (!style) { return; }
for (var prop in style) {
if (!cssNumber[prop] && typeof style[prop] == 'number') {
style[prop] += 'px';
}
}
};
}
module.exports = exports['default'];
});
var jssPx = unwrapExports(index$3);
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var isBrowser = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && (typeof document === "undefined" ? "undefined" : _typeof(document)) === 'object' && document.nodeType === 9;
var module$1 = Object.freeze({
isBrowser: isBrowser,
default: isBrowser
});
var _isInBrowser = ( module$1 && isBrowser ) || module$1;
var prefix = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _isInBrowser2 = _interopRequireDefault(_isInBrowser);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var js = ''; /**
* Export javascript style and css style vendor prefixes.
* Based on "transform" support test.
*/
var css = '';
// We should not do anything if required serverside.
if (_isInBrowser2['default']) {
// Order matters. We need to check Webkit the last one because
// other vendors use to add Webkit prefixes to some properties
var jsCssMap = {
Moz: '-moz-',
// IE did it wrong again ...
ms: '-ms-',
O: '-o-',
Webkit: '-webkit-'
};
var style = document.createElement('p').style;
var testProp = 'Transform';
for (var key in jsCssMap) {
if (key + testProp in style) {
js = key;
css = jsCssMap[key];
break;
}
}
}
/**
* Vendor prefix string for the current browser.
*
* @type {{js: String, css: String}}
* @api public
*/
exports['default'] = { js: js, css: css };
});
var camelize_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports['default'] = camelize;
var regExp = /[-\s]+(.)?/g;
/**
* Convert dash separated strings to camel cased.
*
* @param {String} str
* @return {String}
*/
function camelize(str) {
return str.replace(regExp, toUpper);
}
function toUpper(match, c) {
return c ? c.toUpperCase() : '';
}
});
var supportedProperty_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports['default'] = supportedProperty;
var _isInBrowser2 = _interopRequireDefault(_isInBrowser);
var _prefix2 = _interopRequireDefault(prefix);
var _camelize2 = _interopRequireDefault(camelize_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var el = void 0;
var cache = {};
if (_isInBrowser2['default']) {
el = document.createElement('p');
/**
* We test every property on vendor prefix requirement.
* Once tested, result is cached. It gives us up to 70% perf boost.
* http://jsperf.com/element-style-object-access-vs-plain-object
*
* Prefill cache with known css properties to reduce amount of
* properties we need to feature test at runtime.
* http://davidwalsh.name/vendor-prefix
*/
var computed = window.getComputedStyle(document.documentElement, '');
for (var key in computed) {
if (!isNaN(key)) { cache[computed[key]] = computed[key]; }
}
}
/**
* Test if a property is supported, returns supported property with vendor
* prefix if required. Returns `false` if not supported.
*
* @param {String} prop dash separated
* @return {String|Boolean}
* @api public
*/
function supportedProperty(prop) {
// For server-side rendering.
if (!el) { return prop; }
// We have not tested this prop yet, lets do the test.
if (cache[prop] != null) { return cache[prop]; }
// Camelization is required because we can't test using
// css syntax for e.g. in FF.
// Test if property is supported as it is.
if ((0, _camelize2['default'])(prop) in el.style) {
cache[prop] = prop;
}
// Test if property is supported with vendor prefix.
else if (_prefix2['default'].js + (0, _camelize2['default'])('-' + prop) in el.style) {
cache[prop] = _prefix2['default'].css + prop;
} else {
cache[prop] = false;
}
return cache[prop];
}
});
var supportedValue_1 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports['default'] = supportedValue;
var _isInBrowser2 = _interopRequireDefault(_isInBrowser);
var _prefix2 = _interopRequireDefault(prefix);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var cache = {};
var el = void 0;
if (_isInBrowser2['default']) { el = document.createElement('p'); }
/**
* Returns prefixed value if needed. Returns `false` if value is not supported.
*
* @param {String} property
* @param {String} value
* @return {String|Boolean}
* @api public
*/
function supportedValue(property, value) {
// For server-side rendering.
if (!el) { return value; }
// It is a string or a number as a string like '1'.
// We want only prefixable values here.
if (typeof value !== 'string' || !isNaN(parseInt(value, 10))) { return value; }
var cacheKey = property + value;
if (cache[cacheKey] != null) { return cache[cacheKey]; }
// IE can even throw an error in some cases, for e.g. style.content = 'bar'
try {
// Test value as it is.
el.style[property] = value;
} catch (err) {
cache[cacheKey] = false;
return false;
}
// Value is supported as it is.
if (el.style[property] !== '') {
cache[cacheKey] = value;
} else {
// Test value with vendor prefix.
value = _prefix2['default'].css + value;
// Hardcode test to convert "flex" to "-ms-flexbox" for IE10.
if (value === '-ms-flex') { value = '-ms-flexbox'; }
el.style[property] = value;
// Value is supported with vendor prefix.
if (el.style[property] !== '') { cache[cacheKey] = value; }
}
if (!cache[cacheKey]) { cache[cacheKey] = false; }
// Reset style value.
el.style[property] = '';
return cache[cacheKey];
}
});
var index$5 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.supportedValue = exports.supportedProperty = exports.prefix = undefined;
var _prefix2 = _interopRequireDefault(prefix);
var _supportedProperty2 = _interopRequireDefault(supportedProperty_1);
var _supportedValue2 = _interopRequireDefault(supportedValue_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
exports['default'] = {
prefix: _prefix2['default'],
supportedProperty: _supportedProperty2['default'],
supportedValue: _supportedValue2['default']
}; /**
* CSS Vendor prefix detection and property feature testing.
*
* @copyright Oleg Slobodskoi 2015
* @website https://github.com/jsstyles/css-vendor
* @license MIT
*/
exports.prefix = _prefix2['default'];
exports.supportedProperty = _supportedProperty2['default'];
exports.supportedValue = _supportedValue2['default'];
});
var index$4 = createCommonjsModule(function (module, exports) {
'use strict';
exports.__esModule = true;
exports['default'] = jssVendorPrefixer;
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj['default'] = obj; return newObj; } }
var vendor = _interopRequireWildcard(index$5);
/**
* Add vendor prefix to a property name when needed.
*
* @param {Rule} rule
* @api public
*/
function jssVendorPrefixer() {
return function (rule) {
if (rule.type === 'keyframe') {
rule.selector = '@' + vendor.prefix.css + 'keyframes' + rule.selector.substr(10);
return;
}
if (rule.type !== 'regular') { return; }
for (var prop in rule.style) {
var value = rule.style[prop];
var changeProp = false;
var supportedProp = vendor.supportedProperty(prop);
if (supportedProp && supportedProp !== prop) { changeProp = true; }
var changeValue = false;
var supportedValue = vendor.supportedValue(supportedProp, value);
if (supportedValue && supportedValue !== value) { changeValue = true; }
if (changeProp || changeValue) {
if (changeProp) { delete rule.style[prop]; }
rule.style[supportedProp || prop] = supportedValue || value;
}
}
};
}
module.exports = exports['default'];
});
var jssVendorPrefixer = unwrapExports(index$4);
var index$7 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var regExp = /([A-Z])/g;
/**
* Replace a string passed from String#replace.
* @param {String} str
* @return {String}
*/
function replace(str) {
return '-' + str.toLowerCase();
}
/**
* Convert camel cased properties of a single style to dasherized.
*
* @param {Object} style
* @return {Object} convertedStyle
*/
function convertCase(style) {
var convertedStyle = {};
for (var prop in style) {
var value = style[prop];
prop = prop.replace(regExp, replace);
convertedStyle[prop] = value;
}
return convertedStyle;
}
/**
* Allow camel cased property names by converting them back to dasherized.
*
* @param {Rule} rule
*/
exports.default = function () {
return function jssCamelCase(rule) {
var style = rule.style;
if (!style) { return; }
if (Array.isArray(style)) {
// Handle rules like @font-face, which can have multiple styles in an array
for (var index = 0; index < style.length; index++) {
style[index] = convertCase(style[index]);
}
} else {
rule.style = convertCase(style);
}
};
};
});
var jssCamelCase = unwrapExports(index$7);
var index$8 = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = jssPropsSort;
/**
* Sort props by length.
*
* @param {Rule} rule
* @api public
*/
function jssPropsSort() {
function sort(prop0, prop1) {
return prop0.length > prop1.length;
}
return function (rule) {
var style = rule.style;
var type = rule.type;
if (!style || type !== 'regular') { return; }
var newStyle = {};
var props = Object.keys(style).sort(sort);
for (var prop in props) {
newStyle[props[prop]] = style[props[prop]];
}
rule.style = newStyle;
};
}
});
var jssPropsSort = unwrapExports(index$8);
const jss = Jss.create();
jss.use(jssNested());
jss.use(jssExtend());
jss.use(jssPx());
jss.use(jssVendorPrefixer());
jss.use(jssCamelCase());
jss.use(jssPropsSort());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
var _now = Date.now;
function _timestampTouch(touch, event, history) {
return {
x: touch.clientX,
y: touch.clientY,
identifier : touch.identifier,
origin: event.origin,
timestamp: _now(),
count: event.touches.length,
history: history
};
}
function _handleStart$1(event) {
if (event.touches.length > this.touchLimit) { return; }
this.isTouched = true;
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var data = _timestampTouch(touch, event, null);
this.eventOutput.emit('trackstart', data);
if (!this.selective && !this.touchHistory[touch.identifier]) { this.track(data); }
}
}
function _handleMove$1(event) {
if (event.touches.length > this.touchLimit) { return; }
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var history = this.touchHistory[touch.identifier];
if (history) {
var data = _timestampTouch(touch, event, history);
this.touchHistory[touch.identifier].push(data);
this.eventOutput.emit('trackmove', data);
}
}
}
function _handleEnd$1(event) {
if (!this.isTouched) { return; }
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches[i];
var history = this.touchHistory[touch.identifier];
if (history) {
var data = _timestampTouch(touch, event, history);
this.eventOutput.emit('trackend', data);
delete this.touchHistory[touch.identifier];
}
}
this.isTouched = false;
}
function _handleUnpipe() {
for (var i in this.touchHistory) {
var history = this.touchHistory[i];
this.eventOutput.emit('trackend', {
touch: history[history.length - 1].touch,
timestamp: Date.now(),
count: 0,
history: history
});
delete this.touchHistory[i];
}
}
/**
* Helper to TouchSync – tracks piped in touch events, organizes touch
* events by ID, and emits track events back to TouchSync.
* Emits 'trackstart', 'trackmove', and 'trackend' events upstream.
*
* @class TouchTracker
* @constructor
* @param {Object} options default options overrides
* @param [options.selective] {Boolean} selective if false, saves state for each touch
* @param [options.touchLimit] {Number} touchLimit upper bound for emitting events based on number of touches
*/
function TouchTracker(options) {
this.selective = options.selective;
this.touchLimit = options.touchLimit || 1;
this.touchHistory = {};
this.eventInput = new EventHandler_1();
this.eventOutput = new EventHandler_1();
EventHandler_1.setInputHandler(this, this.eventInput);
EventHandler_1.setOutputHandler(this, this.eventOutput);
this.eventInput.on('touchstart', _handleStart$1.bind(this));
this.eventInput.on('touchmove', _handleMove$1.bind(this));
this.eventInput.on('touchend', _handleEnd$1.bind(this));
this.eventInput.on('touchcancel', _handleEnd$1.bind(this));
this.eventInput.on('unpipe', _handleUnpipe.bind(this));
this.isTouched = false;
}
/**
* Record touch data, if selective is false.
* @private
* @method track
* @param {Object} data touch data
*/
TouchTracker.prototype.track = function track(data) {
this.touchHistory[data.identifier] = [data];
};
var TouchTracker_1 = TouchTracker;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* Handles piped in touch events. Emits 'start', 'update', and 'events'
* events with delta, position, velocity, acceleration, clientX, clientY, count, and touch id.
* Useful for dealing with inputs on touch devices. Designed to be used either as standalone, or
* included in a GenericSync.
*
* @class TouchSync
* @constructor
*
* @example
* var Surface = require('../core/Surface');
* var TouchSync = require('../inputs/TouchSync');
*
* var surface = new Surface({ size: [100, 100] });
* var touchSync = new TouchSync();
* surface.pipe(touchSync);
*
* touchSync.on('start', function (e) { // react to start });
* touchSync.on('update', function (e) { // react to update });
* touchSync.on('end', function (e) { // react to end });*
*
* @param [options] {Object} default options overrides
* @param [options.direction] {Number} read from a particular axis
* @param [options.rails] {Boolean} read from axis with greatest differential
* @param [options.velocitySampleLength] {Number} Number of previous frames to check velocity against.
* @param [options.scale] {Number} constant factor to scale velocity output
* @param [options.touchLimit] {Number} touchLimit upper bound for emitting events based on number of touches
*/
function TouchSync(options) {
this.options = Object.create(TouchSync.DEFAULT_OPTIONS);
this._optionsManager = new OptionsManager_1(this.options);
if (options) { this.setOptions(options); }
this._eventOutput = new EventHandler_1();
this._touchTracker = new TouchTracker_1({
touchLimit: this.options.touchLimit
});
EventHandler_1.setOutputHandler(this, this._eventOutput);
EventHandler_1.setInputHandler(this, this._touchTracker);
this._touchTracker.on('trackstart', _handleStart.bind(this));
this._touchTracker.on('trackmove', _handleMove.bind(this));
this._touchTracker.on('trackend', _handleEnd.bind(this));
this._payload = {
delta : null,
position : null,
velocity : null,
clientX : undefined,
clientY : undefined,
count : 0,
touch : undefined
};
this._position = null; // to be deprecated
}
TouchSync.DEFAULT_OPTIONS = {
direction: undefined,
rails: false,
touchLimit: 1,
velocitySampleLength: 10,
scale: 1
};
TouchSync.DIRECTION_X = 0;
TouchSync.DIRECTION_Y = 1;
var MINIMUM_TICK_TIME = 8;
/**
* Triggered by trackstart.
* @method _handleStart
* @private
*/
function _handleStart(data) {
var velocity;
var delta;
if (this.options.direction !== undefined){
this._position = 0;
velocity = 0;
delta = 0;
}
else {
this._position = [0, 0];
velocity = [0, 0];
delta = [0, 0];
}
var payload = this._payload;
payload.delta = delta;
payload.position = this._position;
payload.velocity = velocity;
payload.clientX = data.x;
payload.clientY = data.y;
payload.count = data.count;
payload.touch = data.identifier;
this._eventOutput.emit('start', payload);
}
/**
* Triggered by trackmove.
* @method _handleMove
* @private
*/
function _handleMove(data) {
var history = data.history;
var currHistory = history[history.length - 1];
var prevHistory = history[history.length - 2];
var distantHistory = history[history.length - this.options.velocitySampleLength] ?
history[history.length - this.options.velocitySampleLength] :
history[history.length - 2];
var distantTime = distantHistory.timestamp;
var currTime = currHistory.timestamp;
var diffX = currHistory.x - prevHistory.x;
var diffY = currHistory.y - prevHistory.y;
var velDiffX = currHistory.x - distantHistory.x;
var velDiffY = currHistory.y - distantHistory.y;
if (this.options.rails) {
if (Math.abs(diffX) > Math.abs(diffY)) { diffY = 0; }
else { diffX = 0; }
if (Math.abs(velDiffX) > Math.abs(velDiffY)) { velDiffY = 0; }
else { velDiffX = 0; }
}
var diffTime = Math.max(currTime - distantTime, MINIMUM_TICK_TIME);
var velX = velDiffX / diffTime;
var velY = velDiffY / diffTime;
var scale = this.options.scale;
var nextVel;
var nextDelta;
if (this.options.direction === TouchSync.DIRECTION_X) {
nextDelta = scale * diffX;
nextVel = scale * velX;
this._position += nextDelta;
}
else if (this.options.direction === TouchSync.DIRECTION_Y) {
nextDelta = scale * diffY;
nextVel = scale * velY;
this._position += nextDelta;
}
else {
nextDelta = [scale * diffX, scale * diffY];
nextVel = [scale * velX, scale * velY];
this._position[0] += nextDelta[0];
this._position[1] += nextDelta[1];
}
var payload = this._payload;
payload.delta = nextDelta;
payload.velocity = nextVel;
payload.position = this._position;
payload.clientX = data.x;
payload.clientY = data.y;
payload.count = data.count;
payload.touch = data.identifier;
this._eventOutput.emit('update', payload);
}
/**
* Triggered by trackend.
* @method _handleEnd
* @private
*/
function _handleEnd(data) {
this._payload.count = data.count;
this._eventOutput.emit('end', this._payload);
}
/**
* Set internal options, overriding any default options
*
* @method setOptions
*
* @param [options] {Object} default options overrides
* @param [options.direction] {Number} read from a particular axis
* @param [options.rails] {Boolean} read from axis with greatest differential
* @param [options.scale] {Number} constant factor to scale velocity output
*/
TouchSync.prototype.setOptions = function setOptions(options) {
return this._optionsManager.setOptions(options);
};
/**
* Return entire options dictionary, including defaults.
*
* @method getOptions
* @return {Object} configuration options
*/
TouchSync.prototype.getOptions = function getOptions() {
return this.options;
};
var TouchSync_1 = TouchSync;
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Owner: mark@famo.us
* @license MPL 2.0
* @copyright Famous Industries, Inc. 2015
*/
/**
* Combines multiple types of sync classes (e.g. mouse, touch,
* scrolling) into one standardized interface for inclusion in widgets.
*
* Sync classes are first registered with a key, and then can be accessed
* globally by key.
*
* Emits 'start', 'update' and 'end' events as a union of the sync class
* providers.
*
* @class GenericSync
* @constructor
* @param syncs {Object|Array} object with fields {sync key : sync options}
* or an array of registered sync keys
* @param [options] {Object|Array} options object to set on all syncs
*/
function GenericSync(syncs, options) {
this._eventInput = new EventHandler_1();
this._eventOutput = new EventHandler_1();
EventHandler_1.setInputHandler(this, this._eventInput);
EventHandler_1.setOutputHandler(this, this._eventOutput);
this._syncs = {};
if (syncs) { this.addSync(syncs); }
if (options) { this.setOptions(options); }
}
GenericSync.DIRECTION_X = 0;
GenericSync.DIRECTION_Y = 1;
GenericSync.DIRECTION_Z = 2;
// Global registry of sync classes. Append only.
var registry = {};
/**
* Register a global sync class with an identifying key
*
* @static
* @method register
*
* @param syncObject {Object} an object of {sync key : sync options} fields
*/
GenericSync.register = function register(syncObject) {
for (var key in syncObject){
if (registry[key]){ // skip redundant registration
if (registry[key] !== syncObject[key]) // only if same registered class
{ throw new Error('Conflicting sync classes for key: ' + key); }
}
else { registry[key] = syncObject[key]; }
}
};
/**
* Helper to set options on all sync instances
*
* @method setOptions
* @param options {Object} options object
*/
GenericSync.prototype.setOptions = function(options) {
for (var key in this._syncs){
this._syncs[key].setOptions(options);
}
};
/**
* Pipe events to a sync class
*
* @method pipeSync
* @param key {String} identifier for sync class
*/
GenericSync.prototype.pipeSync = function pipeToSync(key) {
var sync = this._syncs[key];
this._eventInput.pipe(sync);
sync.pipe(this._eventOutput);
};
/**
* Unpipe events from a sync class
*
* @method unpipeSync
* @param key {String} identifier for sync class
*/
GenericSync.prototype.unpipeSync = function unpipeFromSync(key) {
var sync = this._syncs[key];
this._eventInput.unpipe(sync);
sync.unpipe(this._eventOutput);
};
function _addSingleSync(key, options) {
if (!registry[key]) { return; }
this._syncs[key] = new (registry[key])(options);
this.pipeSync(key);
}
/**
* Add a sync class to from the registered classes
*
* @method addSync
* @param syncs {Object|Array.String} an array of registered sync keys
* or an object with fields {sync key : sync options}
*/
GenericSync.prototype.addSync = function addSync(syncs) {
if (syncs instanceof Array)
{ for (var i = 0; i < syncs.length; i++)
{ _addSingleSync.call(this, syncs[i]); } }
else if (syncs instanceof Object)
{ for (var key in syncs)
{ _addSingleSync.call(this, key, syncs[key]); } }
};
var GenericSync_1 = GenericSync;
var callAfter_1 = createCommonjsModule(function (module, exports) {
"use strict";
exports.callAfter = callAfter;
function callAfter(times, callback) {
var count = 0;
return function () {
if (++count == times) {
if (typeof callback == "function") {
callback.apply(this, arguments);
}
}
};
}
exports["default"] = callAfter;
exports.__esModule = true;
});
var callAfter = unwrapExports(callAfter_1);
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* A scenegraph with two Molecule leafnodes: the menu area and the content
* area. The menu area is hidden beyond the edge of the screen while the
* content area is visible. Swiping in from the edge of the screen reveals the
* menu, putting the content area out of focus. A mouse can also be used, and
* hovering near the edge of the screen also reveals the menu.
*
* Note: This layout is mostly useful if it exists at the root of a context so
* that the menu is clipped when it is closed (off to the side), otherwise the
* menu will be visible beyond the boundary of the container that contains the
* PushMenuLayout.
*
* Note: If you've called `openMenu` or `closeMenu` with a callback, the callback
* will be canceled if a drag or hover on the menu happens before the animation
* has completed. Please open an issue on GitHub if you have any opinion
* against this. :) Maybe we can add a boolean option for this behavior.
*
* TODO: Embed working example here.
*
* @class PushMenuLayout
* @extends Molecule
*/
var PushMenuLayout = (function (Molecule$$1) {
function PushMenuLayout(initialOptions) {
Molecule$$1.call(this, initialOptions);
// Add default values for this PushMenuLayout
// TODO: Make default options static for the class.
simpleExtend(this._.defaultOptions, {
menuSide: 'left', // left or right
menuWidth: 200,
menuHintSize: 10, // the amount of the menu that is visible before opening the menu.
pushAreaWidth: 40, // the area on the screen edge that the user can touch and drag to push out the menu.
animationDuration: 1000,
animationType: 'foldDown', // options: foldDown moveBack
// TODO: Background color for the whole layout should be the color that the fade fades to.
// TODO: Replace fade star/end colors with a fog color value and intensity.
fade: true, // when content recedes, it fades into the fog.
fadeStartColor: 'rgba(255,255,255,0)',
fadeEndColor: 'rgba(255,255,255,1)',
blur: false, // XXX: WIP, so false by default.
blurRadius: 5
});
// TODO: performance hit, this setter is invoked in the Molecule constructor, then here again.
this.options = initialOptions;
// TODO v0.1.0: Mark these as private.
// TODO v0.1.0: this.contentWidth should be the width of whatever is containing
// the layout, but we're just using it as a whole-page app for now. Get
// size from a commit? UPDATE: See the new famous/src/views/SizeAwareView
this.contentWidth = document.body.clientWidth - this.options.menuHintSize;
// Changing these values outside of an instance of PushMenuLayout might
// cause the layout to break. They are designed to be modified
// internally only.
this.isOpen = false;
this.isOpening = false;
this.isClosing = false;
this.isAnimating = false; // keep track of whether the menu is opening or closing.
this.isBeingDragged = false; // whether the user is dragging/pushing the menu or not.
this.transitionCallback = undefined; // holds the callback to the current open or close menu animation.
// Set the touch sync for pulling/pushing the menu open/closed.
GenericSync_1.register({
touch: TouchSync_1
});
this._createComponents();
this._initializeEvents();
}
if ( Molecule$$1 ) PushMenuLayout.__proto__ = Molecule$$1;
PushMenuLayout.prototype = Object.create( Molecule$$1 && Molecule$$1.prototype );
PushMenuLayout.prototype.constructor = PushMenuLayout;
/**
* See Molecule.setOptions
*
* @override
*/
PushMenuLayout.prototype.setOptions = function setOptions (newOptions) {
Molecule$$1.prototype.setOptions.call(this, newOptions);
};
/**
* See Molecule.resetOptions
*
* @override
*/
PushMenuLayout.prototype.resetOptions = function resetOptions () {
Molecule$$1.prototype.resetOptions.call(this);
};
/**
* Creates the menu area, content area, `Plane` for the fade effect, etc.
*
* @private
*/
PushMenuLayout.prototype._createComponents = function _createComponents () {
var layout = this;
this.touchSync = new GenericSync_1(['touch']);
this.alignment = (this.options.menuSide == "left"? 0: 1);
this.animationTransition = new Transitionable_1(0);
this.mainMol = new Molecule$$1();
this.menuMol = new Molecule$$1({
size: [this.options.menuWidth,undefined]
});
this.menuMol.oldTransform = this.menuMol.transform;
this.menuMol.transform = function() { // override
var currentPosition = layout.animationTransition.get();
switch(layout.options.animationType) {
case "foldDown":
// XXX: this is depending on my modifications for TransitionableTransform.
this.oldTransform.setTranslateX(
layout.options.menuSide == 'left'?
currentPosition * (layout.options.menuWidth-layout.options.menuHintSize)/*range*/ - (layout.options.menuWidth-layout.options.menuHintSize)/*offset*/:
currentPosition * -(layout.options.menuWidth-layout.options.menuHintSize)/*range*/ + (layout.options.menuWidth-layout.options.menuHintSize)/*offset*/
);
break;
case "moveBack":
// XXX: this is depending on my modifications for TransitionableTransform.
this.oldTransform.setTranslateX(
layout.options.menuSide == 'left'?
currentPosition * (layout.options.menuWidth-layout.options.menuHintSize)/*range*/ - (layout.options.menuWidth-layout.options.menuHintSize)/*offset*/:
currentPosition * -(layout.options.menuWidth-layout.options.menuHintSize)/*range*/ + (layout.options.menuWidth-layout.options.menuHintSize)/*offset*/
);
break;
}
return this.oldTransform.get();
}.bind(this.menuMol);
// contains the user's menu content.
this.menuContentMol = new Molecule$$1();
this.contentMol = new Molecule$$1({
size: [this.contentWidth,undefined]
});
this.contentMol.oldTransform = this.contentMol.transform;
this.contentMol.transform = function() { // override
var currentPosition = layout.animationTransition.get();
switch(layout.options.animationType) {
case "foldDown":
// XXX: this is depending on my modifications for TransitionableTransform.
this.oldTransform.setTranslateX(
layout.options.menuSide == 'left'?
currentPosition * (layout.options.menuWidth - layout.options.menuHintSize)/*range*/ + layout.options.menuHintSize/*offset*/:
currentPosition * -(layout.options.menuWidth - layout.options.menuHintSize)/*range*/ - layout.options.menuHintSize/*offset*/
);
// XXX: this is depending on my modifications for TransitionableTransform.
this.oldTransform.setRotateY(
layout.options.menuSide == 'left'?
currentPosition * Math.PI/8:
currentPosition * -Math.PI/8
);
break;
case "moveBack":
var depth = 100;
// XXX: this is depending on my modifications for TransitionableTransform.
this.oldTransform.setTranslateX(
layout.options.menuSide == 'left'?
layout.options.menuHintSize:
-layout.options.menuHintSize
);
this.oldTransform.setTranslateZ(
currentPosition * -depth
);
break;
}
return this.oldTransform.get();
}.bind(this.contentMol);
this.menuTouchPlane = new Plane({
size: [this.options.menuWidth + this.options.pushAreaWidth - this.options.menuHintSize, undefined],
properties: {
zIndex: '-1000' // below everything
}
});
this.mainMol.setOptions({
origin: [this.alignment, 0.5],
align: [this.alignment, 0.5]
});
this.menuMol.setOptions({
origin: [this.alignment, 0.5],
align: [this.alignment, 0.5]
});
this.contentMol.setOptions({
origin: [this.alignment, 0.5],
align: [this.alignment, 0.5]
});
// FIXME: WHY THE EFF must I also set align and origin on menuTouchPlane
// when I've already set it on it's parent (this.menuMol)?????
this.menuTouchPlane.setOptions({
origin: [this.alignment, 0.5],
align: [this.alignment, 0.5]
});
// Bring the menu content molecule and touch plane forward just
// slightly so they're just above the content and content's fade plane,
// so touch and mouse interaction works. HTML, the bad parts. ;)
this.menuContentMol.transform.setTranslateZ(2);
this.menuTouchPlane.transform.setTranslateZ(2);
/*
* Styles for the fadePlane
*/
// TODO: move this somewhere else . it's specific for each animation
this.updateStyles = function() {
var startColor;
var endColor;
switch(this.options.animationType) {
case "foldDown":
startColor = this.options.fadeStartColor;
endColor = this.options.fadeEndColor;
break;
case "moveBack":
startColor = endColor = this.options.fadeEndColor;
break;
}
var styles = {
'.infamous-fadeLeft': {
background: [
endColor,
'-moz-linear-gradient(left, '+endColor+' 0%, '+startColor+' 100%)',
'-webkit-gradient(left top, right top, color-stop(0%, '+endColor+'), color-stop(100%, '+startColor+'))',
'-webkit-linear-gradient(left, '+endColor+' 0%, '+startColor+' 100%)',
'-o-linear-gradient(left, '+endColor+' 0%, '+startColor+' 100%)',
'-ms-linear-gradient(left, '+endColor+' 0%, '+startColor+' 100%)',
'linear-gradient(to right, '+endColor+' 0%, '+startColor+' 100%)'
],
filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#cc000000\', endColorstr=\'#4d000000\', GradientType=1 )'
},
'.infamous-fadeRight': {
background: [
startColor,
'-moz-linear-gradient(left, '+startColor+' 0%, '+endColor+' 100%)',
'-webkit-gradient(left top, right top, color-stop(0%, '+startColor+'), color-stop(100%, '+endColor+'))',
'-webkit-linear-gradient(left, '+startColor+' 0%, '+endColor+' 100%)',
'-o-linear-gradient(left, '+startColor+' 0%, '+endColor+' 100%)',
'-ms-linear-gradient(left, '+startColor+' 0%, '+endColor+' 100%)',
'linear-gradient(to right, '+startColor+' 0%, '+endColor+' 100%)'
],
filter: 'progid:DXImageTransform.Microsoft.gradient( startColorstr=\'#4d000000\', endColorstr=\'#cc000000\', GradientType=1 )'
}
};
if (this.fadeStylesheet) { this.fadeStylesheet.detach(); }
this.fadeStylesheet = jss.createStyleSheet(styles);
this.fadeStylesheet.attach();
};
if (this.options.fade) {
this.updateStyles();
this.fadePlane = new Plane({
size: [undefined,undefined],
classes: [
// TODO: switch to jss namespace.
(this.options.menuSide == 'left'? 'infamous-fadeRight': 'infamous-fadeLeft')
],
properties: {
zIndex: '1000',
pointerEvents: 'none'
}
});
// FIXME: Why the EFF must I also set align and origin on fadePlane when
// I've already set it on it's parent (this.contentMol)?????
this.fadePlane.setOptions({
origin: [this.alignment, 0.5],
align: [this.alignment, 0.5]
});
// move the fadePlane forward by 1px so it doesn't glitch out.
// Chrome will make the fadePlane and the surface in the content
// area (if any) blink randomly when the two surfaces are in the
// same exact position together.
this.fadePlane.transform.setTranslateZ(1);
this.fadePlane.setOptions({
opacity: this.animationTransition
});
// TODO: Make fadePlane a sibling to menuMol and contentMol so that
// contentMol contains only the user;s content. This will affect
// the code in this.render().
this.contentMol.add(this.fadePlane);
}
this.add(this.mainMol);
this.mainMol.add(this.contentMol);
this.menuMol.add(this.menuTouchPlane);
this.menuMol.add(this.menuContentMol);
this.mainMol.add(this.menuMol);
// TODO: Also create and add a background plane for the menu area so it will catch events that might fall through the menu content.
};
/**
* Sets up the events for the touch and mouse interaction that opens and
* closes the menu.
*
* @private
*/
PushMenuLayout.prototype._initializeEvents = function _initializeEvents () {
// move the menu, following the user's drag. Don't let the user drag the menu past the menu width.
this.options.handler.on('update', function(event) { // update == drag
this.isBeingDragged = true;
// stop the current transitions if any, along with the current callback if any.
this._haltAnimation(true);
var currentPosition = this.animationTransition.get();
// TODO: handle the right-side menu.
switch(this.options.animationType) {
case "foldDown":
this.animationTransition.set(currentPosition + event.delta[0] / (this.options.menuWidth - this.options.menuHintSize));
break;
case "moveBack":
this.animationTransition.set(currentPosition + event.delta[0] / (this.options.menuWidth - this.options.menuHintSize));
break;
}
currentPosition = this.animationTransition.get();
if (currentPosition > 1) {
this.animationTransition.set(1);
}
else if (currentPosition < 0) {
this.animationTransition.set(0);
}
}.bind(this));
this.options.handler.on('end', function(event) {
this.isBeingDragged = false;
var currentPosition = this.animationTransition.get();
if (currentPosition < 0.5) {
this.closeMenu();
}
else {
this.openMenu();
}
}.bind(this));
// TODO v0.1.0: Use a SizeAwareView instead of relying on the body, since we
// might not be directly in the body.
window.addEventListener('resize', function(event) {
this.contentWidth = document.body.clientWidth - this.options.menuHintSize;
this.contentMol.setOptions({size: [this.contentWidth, undefined]});
}.bind(this));
/*
* Wire up events
* TODO: consolidate dup code here and in setMenu
*/
this.menuTouchPlane.pipe(this.touchSync);
this.menuTouchPlane.on('mouseenter', function() {
if (!this.isOpening) {
this.openMenu();
}
}.bind(this));
this.menuTouchPlane.on('mouseleave', function() {
if (!this.isClosing) {
this.closeMenu();
}
}.bind(this));
this.touchSync.pipe(this.options.handler);
};
/**
* Add a scenegraph to the content area of the PushMenuLayout. You can put
* anything you want into the content area (magical 3D things for example),
* just be careful not to let them cover the menu or you'll block the user
* from interacting with the menu.
*
* @param {module: famous/src/core/RenderNode} node A scenegraph, i.e. a
* RenderNode with stuff in it.
*
* TODO: Accept plain renderables, f.e. Surfaces, etc. This change requires
* also modifying the code in this.render() to account for renderables.
*
* TODO: Make a sibling method to reset the content area.
*/
PushMenuLayout.prototype.setContent = function setContent (node) {
this.contentMol.add(node);
};
/**
* Add a scenegraph to the menu area of the PushMenuLayout. If the object
* that you pass into setMenu is an infamous component, or a famo.us
* Surface, then it's events will be piped to this PushMenuLayout's input
* sync so that the user can open and close the menu with touch or mouse.
* General advice here would be to keep whatever you put into the menu
* contained within the boundaries of the menu or you might have touch and
* mouse interaction outside of the menu.
*
* @param {module: famous/src/core/RenderNode} node A scenegraph, i.e. a
* RenderNode with stuff in it.
*
* TODO: Accept plain renderables, f.e. Surfaces, etc.
*
* TODO: Remove old content before adding new content.
*/
PushMenuLayout.prototype.setMenu = function setMenu (node) {
this.menuContentMol.add(node);
if (node instanceof Molecule$$1) {
node.pipe(this.touchSync);
node.on('mouseenter', function() {
if (!this.isOpening) {
this.openMenu();
}
}.bind(this));
node.on('mouseleave', function() {
if (!this.isClosing) {
this.closeMenu();
}
}.bind(this));
}
};
// TODO: replace menu easing with physics so the user can throw the menu,
// using initial velocity and drag to slow it down, and stop immediately
// when it hits the fully-open or fully-closed positions.
/**
* Opens the menu.
*
* @param {Function} callback The function to be called when the animation finishes.
* @param {boolean} [cancelPreviousCallback=false] This is optional. If
* true, then the callback of a previous open or close animation will be
* canceled if that animation was still inprogress when this method is
* called, otherwise the callback of the previous open or close animation
* will be fired immediately before the animation for this animation begins.
*/
PushMenuLayout.prototype.openMenu = function openMenu (callback, cancelPreviousCallback) {
this._haltAnimation(cancelPreviousCallback);
this.isClosing = false;
this.isOpening = true;
this._animate('open', callback);
};
/**
* Closes the menu.
*
* @param {Function} callback The function to be called when the animation finishes.
* @param {boolean} [cancelPreviousCallback=false] This is optional. If
* true, then the callback of a previous open or close animation will be
* canceled if that animation was still inprogress when this method is
* called, otherwise the callback of the previous open or close animation
* will be fired immediately before the animation for this animation begins.
*/
PushMenuLayout.prototype.closeMenu = function closeMenu (callback, cancelPreviousCallback) {
this._haltAnimation(cancelPreviousCallback);
this.isClosing = true;
this.isOpening = false;
this._animate('close', callback);
};
/**
* Toggles the menu open or closed. If the menu is open or is opening, then it will now start
* closing, and vice versa.
*
* @param {Function} callback The function to be called when the animation finishes.
* @param {boolean} [cancelPreviousCallback=false] This is optional. If
* true, then the callback of a previous open or close animation will be
* canceled if that animation was still inprogress when this method is
* called, otherwise the callback of the previous open or close animation
* will be fired immediately before the animation for this animation begins.
*/
PushMenuLayout.prototype.toggleMenu = function toggleMenu (callback, cancelPreviousCallback) {
if (this.isOpen || this.isOpening) {
this.closeMenu(callback, cancelPreviousCallback);
}
else if (!this.isOpen || this.isClosing) {
this.openMenu(callback, cancelPreviousCallback);
}
};
/**
* Animates the menu to it's target state.
*
* @private
* @param {String} targetState The name of the state to animate to.
* @param {Function} callback The function to call after the animation completes.
*/
PushMenuLayout.prototype._animate = function _animate (targetState, callback) {
this.isAnimating = true;
this.transitionCallback = callback;
var _callback;
var self = this;
function setupCallback(numberOfTransitions) {
// Fire callback after numberOfTransitions calls, when the 4 transitions are complete.
_callback = callAfter(numberOfTransitions, function() {
self.isAnimating = self.isOpening = self.isClosing = false;
self.isOpen = targetState == 'open'? true: false;
if (typeof self.transitionCallback == 'function') {
self.transitionCallback();
}
self.transitionCallback = undefined;
}.bind(self));
}
setupCallback(1);
if (targetState == 'open') {
this.animationTransition.set(1, {duration: this.options.animationDuration, curve: Easing_1.outExpo}, _callback);
}
else if (targetState == 'close') {
this.animationTransition.set(0, {duration: this.options.animationDuration, curve: Easing_1.outExpo}, _callback);
}
};
/**
* Halts the current animation, if any.
*
* @private
* @param {boolean} [cancelCallback=false] Defaults to false. If true, the
* halted animation's callback won't fire, otherwise it will be fired.
*/
PushMenuLayout.prototype._haltAnimation = function _haltAnimation (cancelCallback) {
if (this.isAnimating) {
if (!cancelCallback && typeof this.transitionCallback == 'function') {
this.transitionCallback();
}
this.transitionCallback = undefined;
this.animationTransition.halt();
}
};
/**
* @override
*/
PushMenuLayout.prototype.render = function render () {
// Blur the content if this.options.blur is true, and the animation is moveBack.
//
// TODO: Make the item to to be blur specifiable, perhaps with a method on
// this.
if (this.options.blur && this.options.fade && this.options.animationType == 'moveBack') {
let momentaryBlur = (this.animationTransition.get() * this.options.blurRadius);
let filter = {
"-webkit-filter": 'blur('+momentaryBlur+'px)',
"-moz-filter": 'blur('+momentaryBlur+'px)',
"-ms-filter": 'blur('+momentaryBlur+'px)',
"-o-filter": 'blur('+momentaryBlur+'px)',
filter: 'blur('+momentaryBlur+'px)'
};
// TODO TODO TODO v0.1.0: Make fadePlane a sibling with menu and
// content molecules or the following breaks if fade is false.
// Then remove the check for this.options.fade in the previous if
// statement above.
if (this.contentMol._child[1].get() instanceof Surface_1) {
this.contentMol.get().setProperties(filter);
}
else if (this.contentMol._child[1] instanceof Plane) {
this.contentMol._child[1].surface.setProperties(filter);
}
}
return Molecule$$1.prototype.render.call(this)
};
return PushMenuLayout;
}(Molecule));
// A reusable array, to avoid allocating new arrays during multiplication.
// in column-major order:
const scratch = [
/*m11*/0, /*m12*/0, /*m13*/0, /*m14*/0,
/*m21*/0, /*m22*/0, /*m23*/0, /*m24*/0,
/*m31*/0, /*m32*/0, /*m33*/0, /*m34*/0,
/*m41*/0, /*m42*/0, /*m43*/0, /*m44*/0 ];
function multiplyAndApply(A, B, target) {
//XXX: Are the following calculations faster hard coded (current), or as a loop?
scratch[0] = (A.m11 * B.m11) + (A.m21 * B.m12) + (A.m31 * B.m13) + (A.m41 * B.m14);
scratch[4] = (A.m11 * B.m21) + (A.m21 * B.m22) + (A.m31 * B.m23) + (A.m41 * B.m24);
scratch[8] = (A.m11 * B.m31) + (A.m21 * B.m32) + (A.m31 * B.m33) + (A.m41 * B.m34);
scratch[12] = (A.m11 * B.m41) + (A.m21 * B.m42) + (A.m31 * B.m43) + (A.m41 * B.m44);
scratch[1] = (A.m12 * B.m11) + (A.m22 * B.m12) + (A.m32 * B.m13) + (A.m42 * B.m14);
scratch[5] = (A.m12 * B.m21) + (A.m22 * B.m22) + (A.m32 * B.m23) + (A.m42 * B.m24);
scratch[9] = (A.m12 * B.m31) + (A.m22 * B.m32) + (A.m32 * B.m33) + (A.m42 * B.m34);
scratch[13] = (A.m12 * B.m41) + (A.m22 * B.m42) + (A.m32 * B.m43) + (A.m42 * B.m44);
scratch[2] = (A.m13 * B.m11) + (A.m23 * B.m12) + (A.m33 * B.m13) + (A.m43 * B.m14);
scratch[6] = (A.m13 * B.m21) + (A.m23 * B.m22) + (A.m33 * B.m23) + (A.m43 * B.m24);
scratch[10] = (A.m13 * B.m31) + (A.m23 * B.m32) + (A.m33 * B.m33) + (A.m43 * B.m34);
scratch[14] = (A.m13 * B.m41) + (A.m23 * B.m42) + (A.m33 * B.m43) + (A.m43 * B.m44);
scratch[3] = (A.m14 * B.m11) + (A.m24 * B.m12) + (A.m34 * B.m13) + (A.m44 * B.m14);
scratch[7] = (A.m14 * B.m21) + (A.m24 * B.m22) + (A.m34 * B.m23) + (A.m44 * B.m24);
scratch[11] = (A.m14 * B.m31) + (A.m24 * B.m32) + (A.m34 * B.m33) + (A.m44 * B.m34);
scratch[15] = (A.m14 * B.m41) + (A.m24 * B.m42) + (A.m34 * B.m43) + (A.m44 * B.m44);
applyArrayValuesToDOMMatrix(scratch, target);
}
function applyArrayValuesToDOMMatrix(array, matrix) {
const length = array.length;
if (length === 6) {
matrix.m11 = array[0];
matrix.m12 = array[1];
matrix.m21 = array[2];
matrix.m22 = array[3];
matrix.m41 = array[4];
matrix.m42 = array[5];
}
else if (length === 16) {
matrix.m11 = array[0];
matrix.m12 = array[1];
matrix.m13 = array[2];
matrix.m14 = array[3];
matrix.m21 = array[4];
matrix.m22 = array[5];
matrix.m23 = array[6];
matrix.m24 = array[7];
matrix.m31 = array[8];
matrix.m32 = array[9];
matrix.m33 = array[10];
matrix.m34 = array[11];
matrix.m41 = array[12];
matrix.m42 = array[13];
matrix.m43 = array[14];
matrix.m44 = array[15];
}
}
function rotateAxisAngleArray(x, y, z, angle) {
var sin = Math.sin;
var cos = Math.cos;
var pow = Math.pow;
const halfAngle = degreesToRadians(angle/2);
// TODO: should we provide a 6-item array here to signify 2D when the
// rotation is about the Z axis (for example when calling rotateSelf)?
// TODO: Performance can be improved by first detecting when x, y, or z of
// the axis are zero or 1, and using a pre-simplified version of the
// folowing math based on that condition.
// TODO: Performance can be improved by using different equations (use trig
// identities to find alternate formulas).
return [
1-2*(y*y + z*z)*pow(sin(halfAngle), 2), 2*(x*y*pow(sin(halfAngle), 2) + z*sin(halfAngle)*cos(halfAngle)), 2*(x*z*pow(sin(halfAngle), 2) - y*sin(halfAngle)*cos(halfAngle)), 0,
2*(x*y*pow(sin(halfAngle), 2) - z*sin(halfAngle)*cos(halfAngle)), 1-2*(x*x + z*z)*pow(sin(halfAngle), 2), 2*(y*z*pow(sin(halfAngle), 2) + x*sin(halfAngle)*cos(halfAngle)), 0,
2*(x*z*pow(sin(halfAngle), 2) + y*sin(halfAngle)*cos(halfAngle)), 2*(y*z*pow(sin(halfAngle), 2) - x*sin(halfAngle)*cos(halfAngle)), 1-2*(x*x + y*y)*pow(sin(halfAngle), 2), 0,
0, 0, 0, 1 ]
}
function degreesToRadians(degrees) {
return Math.PI/180 * degrees
}
// This matrix is represented internally in row-major format so that it is easy
// to look at visually. In a pair of coordinates (as in "m23") the first number
// is the column and the second is the row (so "m23" means column 2 row 3).
const identity = [
/*m11*/1, /*m21*/0, /*m31*/0, /*m41*/0,
/*m12*/0, /*m22*/1, /*m32*/0, /*m42*/0,
/*m13*/0, /*m23*/0, /*m33*/1, /*m43*/0,
/*m14*/0, /*m24*/0, /*m34*/0, /*m44*/1 ];
var DOMMatrixReadOnly = null;
function initDOMMatrixReadOnly() {
if (DOMMatrixReadOnly) { return }
DOMMatrixReadOnly = (function () {
function DOMMatrixReadOnly(numberSequence) {
if ( numberSequence === void 0 ) { numberSequence = []; }
if (!(this instanceof DOMMatrix))
{ throw new TypeError("DOMMatrixReadOnly can't be instantiated directly. Use DOMMatrix instead.") }
var length = numberSequence.length;
if (length === undefined || !(length === 6 || length === 16))
{ throw new TypeError('DOMMatrix constructor argument "numberSequence" must be an array-like with 6 or 16 numbers.') }
this._matrix = new Float64Array(identity);
this._isIdentity = true;
this._is2D = length === 6 ? true : false;
applyArrayValuesToDOMMatrix(numberSequence, this);
}
var prototypeAccessors = { is2D: {},isIdentity: {},a: {},b: {},c: {},d: {},e: {},f: {},m11: {},m12: {},m13: {},m14: {},m21: {},m22: {},m23: {},m24: {},m31: {},m32: {},m33: {},m34: {},m41: {},m42: {},m43: {},m44: {} };
// Immutable transform methods -------------------------------------------
DOMMatrixReadOnly.prototype.translate = function translate (tx, ty, tz) {
if ( tz === void 0 ) { tz = 0; }
return new DOMMatrix(this).translateSelf(tx, ty, tz)
};
DOMMatrixReadOnly.prototype.scale = function scale (scale$1, originX, originY) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
return new DOMMatrix(this).scaleSelf(scale$1, originX, originY)
};
DOMMatrixReadOnly.prototype.scale3d = function scale3d (scale, originX, originY, originZ) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
if ( originZ === void 0 ) { originZ = 0; }
return new DOMMatrix(this).scale3dSelf(scale, originX, originY, originZ)
};
DOMMatrixReadOnly.prototype.scaleNonUniform = function scaleNonUniform (scaleX, scaleY, scaleZ, originX, originY, originZ) {
if ( scaleY === void 0 ) { scaleY = 1; }
if ( scaleZ === void 0 ) { scaleZ = 1; }
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
if ( originZ === void 0 ) { originZ = 0; }
return new DOMMatrix(this).scaleNonUniformSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
};
DOMMatrixReadOnly.prototype.rotate = function rotate (angle, originX, originY) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
return new DOMMatrix(this).rotateSelf(angle, originX, originY)
};
// TODO
DOMMatrixReadOnly.prototype.rotateFromVector = function rotateFromVector (x, y) {
throw new Error('rotateFromVector is not implemented yet.')
};
DOMMatrixReadOnly.prototype.rotateAxisAngle = function rotateAxisAngle (x, y, z, angle) {
return new DOMMatrix(this).rotateAxisAngleSelf(x, y, z, angle)
};
DOMMatrixReadOnly.prototype.skewX = function skewX (sx) {
throw new Error('skewX is not implemented yet.')
};
DOMMatrixReadOnly.prototype.skewY = function skewY (sy) {
throw new Error('skewY is not implemented yet.')
};
DOMMatrixReadOnly.prototype.multiply = function multiply (other) {
return new DOMMatrix(this).multiplySelf(other)
};
DOMMatrixReadOnly.prototype.flipX = function flipX () {
throw new Error('flipX is not implemented yet.')
};
DOMMatrixReadOnly.prototype.flipY = function flipY () {
throw new Error('flipY is not implemented yet.')
};
DOMMatrixReadOnly.prototype.inverse = function inverse () {
throw new Error('inverse is not implemented yet.')
};
DOMMatrixReadOnly.prototype.transformPoint = function transformPoint (/*optional DOMPointInit*/ point) {
throw new Error('transformPoint is not implemented yet.')
};
DOMMatrixReadOnly.prototype.toFloat32Array = function toFloat32Array () {
return Float32Array.from(this._matrix)
};
DOMMatrixReadOnly.prototype.toFloat64Array = function toFloat64Array () {
return Float64Array.from(this._matrix)
};
//stringifier() {} // What's this?
prototypeAccessors.is2D.get = function () {
return this._is2D
};
/*
* TODO: make sure this matches the spec.
* TODO: Instead of calculating here, perhaps calculate and set
* this._isIdentity in other operations, and simply return the internal one
* here.
*/
prototypeAccessors.isIdentity.get = function () {
var this$1 = this;
for (var i = 0, len = this._matrix.length; i < len; i+=1) {
if (this$1._matrix[i] != identity[i])
{ return (this$1._isIdentity = false) }
}
return (this._isIdentity = true)
};
prototypeAccessors.a.get = function () { return this.m11 };
prototypeAccessors.b.get = function () { return this.m12 };
prototypeAccessors.c.get = function () { return this.m21 };
prototypeAccessors.d.get = function () { return this.m22 };
prototypeAccessors.e.get = function () { return this.m41 };
prototypeAccessors.f.get = function () { return this.m42 };
prototypeAccessors.m11.get = function () { return this._matrix[0] };
prototypeAccessors.m12.get = function () { return this._matrix[4] };
prototypeAccessors.m13.get = function () { return this._matrix[8] };
prototypeAccessors.m14.get = function () { return this._matrix[12] };
prototypeAccessors.m21.get = function () { return this._matrix[1] };
prototypeAccessors.m22.get = function () { return this._matrix[5] };
prototypeAccessors.m23.get = function () { return this._matrix[9] };
prototypeAccessors.m24.get = function () { return this._matrix[13] };
prototypeAccessors.m31.get = function () { return this._matrix[2] };
prototypeAccessors.m32.get = function () { return this._matrix[6] };
prototypeAccessors.m33.get = function () { return this._matrix[10] };
prototypeAccessors.m34.get = function () { return this._matrix[14] };
prototypeAccessors.m41.get = function () { return this._matrix[3] };
prototypeAccessors.m42.get = function () { return this._matrix[7] };
prototypeAccessors.m43.get = function () { return this._matrix[11] };
prototypeAccessors.m44.get = function () { return this._matrix[15] };
Object.defineProperties( DOMMatrixReadOnly.prototype, prototypeAccessors );
return DOMMatrixReadOnly;
}());
}
initDOMMatrixReadOnly();
initDOMMatrixReadOnly();
var DOMMatrix = (function (DOMMatrixReadOnly$$1) {
function DOMMatrix(arg) {
const numArgs = arguments.length;
if (numArgs === 0) {
DOMMatrixReadOnly$$1.call(this, [1, 0, 0, 1, 0, 0]);
}
else if (numArgs === 1) {
if (typeof arg == 'string') {
throw new Error('CSS transformList arg not yet implemented.')
// TODO validate that syntax of transformList matches transform-list (http://www.w3.org/TR/css-transforms-1/#typedef-transform-list).
}
else if (arg instanceof DOMMatrix) {
DOMMatrixReadOnly$$1.call(this, arg._matrix);
}
else if (arg instanceof Float32Array || arg instanceof Float64Array || arg instanceof Array) {
DOMMatrixReadOnly$$1.call(this, arg);
}
}
else {
throw new Error('Wrong number of arguments to DOMMatrix constructor.')
}
}
if ( DOMMatrixReadOnly$$1 ) { DOMMatrix.__proto__ = DOMMatrixReadOnly$$1; }
DOMMatrix.prototype = Object.create( DOMMatrixReadOnly$$1 && DOMMatrixReadOnly$$1.prototype );
DOMMatrix.prototype.constructor = DOMMatrix;
var prototypeAccessors = { a: {},b: {},c: {},d: {},e: {},f: {},m11: {},m12: {},m13: {},m14: {},m21: {},m22: {},m23: {},m24: {},m31: {},m32: {},m33: {},m34: {},m41: {},m42: {},m43: {},m44: {} };
// Mutable transform methods
DOMMatrix.prototype.multiplySelf = function multiplySelf (other) {
if (!(other instanceof DOMMatrix))
{ throw new Error('The argument to multiplySelf must be an instance of DOMMatrix') }
// TODO: avoid creating a new array, just apply values directly.
multiplyAndApply(this, other, this);
if (!other.is2D) { this._is2D = false; }
return this
};
DOMMatrix.prototype.preMultiplySelf = function preMultiplySelf (other) {
if (!(other instanceof DOMMatrix))
{ throw new Error('The argument to multiplySelf must be an instance of DOMMatrix') }
// TODO: avoid creating a new array, just apply values directly.
multiplyAndApply(other, this, this);
if (!other.is2D) { this._is2D = false; }
return this
};
DOMMatrix.prototype.translateSelf = function translateSelf (tx, ty, tz) {
if ( tz === void 0 ) { tz = 0; }
// TODO: check args are numbers
if (arguments.length === 1)
{ throw new Error('The first two arguments (X and Y translation values) are required (the third, Z translation, is optional).') }
// http://www.w3.org/TR/2012/WD-css3-transforms-20120911/#Translate3dDefined
const translationMatrix = new DOMMatrix([
// column-major:
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx,ty,tz,1 ]);
this.multiplySelf(translationMatrix);
if (tz != 0) {
this._is2D = false;
}
return this
};
DOMMatrix.prototype.scaleSelf = function scaleSelf (scale, originX, originY) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
this.translateSelf(originX, originY);
this.multiplySelf(new DOMMatrix([
// 2D:
/*a*/scale, /*b*/0,
/*c*/0, /*d*/scale,
/*e*/0, /*f*/0 ]));
this.translateSelf(-originX, -originY);
return this
};
DOMMatrix.prototype.scale3dSelf = function scale3dSelf (scale, originX, originY, originZ) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
if ( originZ === void 0 ) { originZ = 0; }
this.translateSelf(originX, originY, originZ);
this.multiplySelf(new DOMMatrix([
// 3D
scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, scale, 0,
0, 0, 0, 1 ]));
this.translateSelf(-originX, -originY, -originZ);
return this
};
DOMMatrix.prototype.scaleNonUniformSelf = function scaleNonUniformSelf (scaleX, scaleY, scaleZ, originX, originY, originZ) {
if ( scaleY === void 0 ) { scaleY = 1; }
if ( scaleZ === void 0 ) { scaleZ = 1; }
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
if ( originZ === void 0 ) { originZ = 0; }
this.translateSelf(originX, originY, originZ);
this.multiplySelf(new DOMMatrix([
// 3D
scaleX, 0, 0, 0,
0, scaleY, 0, 0,
0, 0, scaleZ, 0,
0, 0, 0, 1 ]));
this.translateSelf(-originX, -originY, -originZ);
if (scaleZ !== 1 || originZ !== 0) { this._is2D = false; }
return this
};
DOMMatrix.prototype.rotateSelf = function rotateSelf (angle, originX, originY) {
if ( originX === void 0 ) { originX = 0; }
if ( originY === void 0 ) { originY = 0; }
this.translateSelf(originX, originY);
// axis of rotation
var ref = [0,0,1];
var x = ref[0];
var y = ref[1];
var z = ref[2]; // We're rotating around the Z axis.
this.rotateAxisAngleSelf(x, y, z, angle);
this.translateSelf(-originX, -originY);
return this
};
// TODO
DOMMatrix.prototype.rotateFromVectorSelf = function rotateFromVectorSelf (x, y) {
throw new Error('rotateFromVectorSelf is not implemented yet.')
};
DOMMatrix.prototype.rotateAxisAngleSelf = function rotateAxisAngleSelf (x, y, z, angle) {
const rotationMatrix = new DOMMatrix(rotateAxisAngleArray(x,y,z,angle));
this.multiplySelf(rotationMatrix);
return this
};
DOMMatrix.prototype.skewXSelf = function skewXSelf (sx) {
throw new Error('skewXSelf is not implemented yet.')
};
DOMMatrix.prototype.skewYSelf = function skewYSelf (sy) {
throw new Error('skewYSelf is not implemented yet.')
};
DOMMatrix.prototype.invertSelf = function invertSelf () {
throw new Error('invertSelf is not implemented yet.')
};
DOMMatrix.prototype.setMatrixValue = function setMatrixValue (/*DOMString*/ transformList) {
throw new Error('setMatrixValue is not implemented yet.')
};
prototypeAccessors.a.get = function () { return this.m11 };
prototypeAccessors.b.get = function () { return this.m12 };
prototypeAccessors.c.get = function () { return this.m21 };
prototypeAccessors.d.get = function () { return this.m22 };
prototypeAccessors.e.get = function () { return this.m41 };
prototypeAccessors.f.get = function () { return this.m42 };
prototypeAccessors.m11.get = function () { return this._matrix[0] };
prototypeAccessors.m12.get = function () { return this._matrix[4] };
prototypeAccessors.m13.get = function () { return this._matrix[8] };
prototypeAccessors.m14.get = function () { return this._matrix[12] };
prototypeAccessors.m21.get = function () { return this._matrix[1] };
prototypeAccessors.m22.get = function () { return this._matrix[5] };
prototypeAccessors.m23.get = function () { return this._matrix[9] };
prototypeAccessors.m24.get = function () { return this._matrix[13] };
prototypeAccessors.m31.get = function () { return this._matrix[2] };
prototypeAccessors.m32.get = function () { return this._matrix[6] };
prototypeAccessors.m33.get = function () { return this._matrix[10] };
prototypeAccessors.m34.get = function () { return this._matrix[14] };
prototypeAccessors.m41.get = function () { return this._matrix[3] };
prototypeAccessors.m42.get = function () { return this._matrix[7] };
prototypeAccessors.m43.get = function () { return this._matrix[11] };
prototypeAccessors.m44.get = function () { return this._matrix[15] };
prototypeAccessors.a.set = function (value) { this.m11 = value; };
prototypeAccessors.b.set = function (value) { this.m12 = value; };
prototypeAccessors.c.set = function (value) { this.m21 = value; };
prototypeAccessors.d.set = function (value) { this.m22 = value; };
prototypeAccessors.e.set = function (value) { this.m41 = value; };
prototypeAccessors.f.set = function (value) { this.m42 = value; };
prototypeAccessors.m11.set = function (value) { this._matrix[0] = value; };
prototypeAccessors.m12.set = function (value) { this._matrix[4] = value; };
prototypeAccessors.m13.set = function (value) { this._matrix[8] = value; };
prototypeAccessors.m14.set = function (value) { this._matrix[12] = value; };
prototypeAccessors.m21.set = function (value) { this._matrix[1] = value; };
prototypeAccessors.m22.set = function (value) { this._matrix[5] = value; };
prototypeAccessors.m23.set = function (value) { this._matrix[9] = value; };
prototypeAccessors.m24.set = function (value) { this._matrix[13] = value; };
prototypeAccessors.m31.set = function (value) { this._matrix[2] = value; };
prototypeAccessors.m32.set = function (value) { this._matrix[6] = value; };
prototypeAccessors.m33.set = function (value) { this._matrix[10] = value; };
prototypeAccessors.m34.set = function (value) { this._matrix[14] = value; };
prototypeAccessors.m41.set = function (value) { this._matrix[3] = value; };
prototypeAccessors.m42.set = function (value) { this._matrix[7] = value; };
prototypeAccessors.m43.set = function (value) { this._matrix[11] = value; };
prototypeAccessors.m44.set = function (value) { this._matrix[15] = value; };
Object.defineProperties( DOMMatrix.prototype, prototypeAccessors );
return DOMMatrix;
}(DOMMatrixReadOnly));
let privatesMap;
const _ = function (o) {
if (!privatesMap) {
privatesMap = new WeakMap;
let privates = {};
privatesMap.set(o, privates);
return privates
}
else {
let privates = privatesMap.get(o);
if (privates === undefined) {
privates = {};
privatesMap.set(o, privates);
}
return privates
}
};
var DOMPointReadOnly = function DOMPointReadOnly(x,y,z,w) {
if (arguments.length === 1) {
if (!isDOMPointInit(x))
{ throw new TypeError('Expected an object with x, y, z, and w properties') }
_(this).x = x.x;
_(this).y = x.y;
_(this).z = x.z;
_(this).w = x.w;
}
else if (arguments.length === 4) {
_(this).x = x || 0;
_(this).y = y || 0;
_(this).z = z || 0;
_(this).w = w || 0;
}
else {
throw new TypeError('Expected 1 or 4 arguments')
}
};
var prototypeAccessors = { x: {},y: {},z: {},w: {} };
prototypeAccessors.x.get = function () { return _(this).x };
prototypeAccessors.y.get = function () { return _(this).y };
prototypeAccessors.z.get = function () { return _(this).z };
prototypeAccessors.w.get = function () { return _(this).w };
DOMPointReadOnly.prototype.matrixTransform = function matrixTransform (matrix) {
let result = new this.constructor(this);
// TODO
//const x
//const y
//const z
//const w
return result
};
DOMPointReadOnly.fromPoint = function fromPoint (other) {
return new this(other)
};
Object.defineProperties( DOMPointReadOnly.prototype, prototypeAccessors );
var DOMPoint = (function (DOMPointReadOnly) {
function DOMPoint () {
DOMPointReadOnly.apply(this, arguments);
}
if ( DOMPointReadOnly ) { DOMPoint.__proto__ = DOMPointReadOnly; }
DOMPoint.prototype = Object.create( DOMPointReadOnly && DOMPointReadOnly.prototype );
DOMPoint.prototype.constructor = DOMPoint;
var prototypeAccessors$1 = { x: {},y: {},z: {},w: {} };
prototypeAccessors$1.x.set = function (value) { _(this).x = value; };
prototypeAccessors$1.y.set = function (value) { _(this).y = value; };
prototypeAccessors$1.z.set = function (value) { _(this).z = value; };
prototypeAccessors$1.w.set = function (value) { _(this).w = value; };
Object.defineProperties( DOMPoint.prototype, prototypeAccessors$1 );
return DOMPoint;
}(DOMPointReadOnly));
function isDOMPointInit(o) {
if (typeof o != 'object') { return false }
if (
'x' in o &&
'y' in o &&
'z' in o &&
'w' in o
) { return true }
return false
}
let _global = null;
// browser
if (typeof window != 'undefined') {
_global = window;
}
else if (typeof global != 'undefined') {
_global = global;
}
if (_global) {
_global.DOMMatrix = DOMMatrix;
_global.DOMMatrixReadOnly = DOMMatrixReadOnly;
_global.DOMPoint = DOMPoint;
_global.DOMPointReadOnly = DOMPointReadOnly;
}
const instanceofSymbol$2 = Symbol('instanceofSymbol');
const ObservableMixin = base => {
var Observable = (function (base) {
function Observable(options) {
if ( options === void 0 ) options = {};
base.call(this, options);
}
if ( base ) Observable.__proto__ = base;
Observable.prototype = Object.create( base && base.prototype );
Observable.prototype.constructor = Observable;
Observable.prototype.on = function on (eventName, callback) {
if (!this._eventMap)
{ this._eventMap = new Map; }
if (!this._eventMap.has(eventName))
{ this._eventMap.set(eventName, []); }
if (typeof callback == 'function')
{ this._eventMap.get(eventName).push(callback); }
else
{ throw new Error('Expected a function in callback argument of Observable#on.') }
};
Observable.prototype.off = function off (eventName, callback) {
if (!this._eventMap || !this._eventMap.has(eventName)) { return }
const callbacks = this._eventMap.get(eventName);
if (callbacks.indexOf(callback) === -1) { return }
callbacks.splice(callbacks.indexOf(callback), 1);
if (callbacks.length === 0) { this._eventMap.delete(eventName); }
if (this._eventMap.size === 0) { this._eventMap = null; }
};
Observable.prototype.triggerEvent = function triggerEvent (eventName, data) {
if (!this._eventMap || !this._eventMap.has(eventName)) { return }
const callbacks = this._eventMap.get(eventName);
for (let i=0, len=callbacks.length; i<len; i+=1) {
callbacks[i](data);
}
};
return Observable;
}(base));
Object.defineProperty(Observable, Symbol.hasInstance, {
value: function(obj) {
if (this !== Observable) { return Object.getPrototypeOf(Observable)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol$2))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
Observable[instanceofSymbol$2] = true;
return Observable
};
const Observable = ObservableMixin((function () {
function anonymous () {}
return anonymous;
}()));
Observable.mixin = ObservableMixin;
/**
* Represents a set of values for the X, Y, and Z axes. For example, the
* position of an object can be described using a set of 3 numbers, one for each
* axis in 3D space: {x:10, y:10, z:10}.
*
* The value don't have to be numerical. For example,
* {x:'foo', y:'bar', z:'baz'}
*/
var XYZValues = (function (Observable$$1) {
function XYZValues(x, y, z) {
if ( x === void 0 ) x = 0;
if ( y === void 0 ) y = 0;
if ( z === void 0 ) z = 0;
Observable$$1.call(this);
this._x = x;
this._y = y;
this._z = z;
}
if ( Observable$$1 ) XYZValues.__proto__ = Observable$$1;
XYZValues.prototype = Object.create( Observable$$1 && Observable$$1.prototype );
XYZValues.prototype.constructor = XYZValues;
var prototypeAccessors = { x: {},y: {},z: {} };
prototypeAccessors.x.set = function (value) {
this._x = value;
this.triggerEvent('valuechanged', {x: value});
};
prototypeAccessors.x.get = function () { return this._x };
prototypeAccessors.y.set = function (value) {
this._y = value;
this.triggerEvent('valuechanged', {y: value});
};
prototypeAccessors.y.get = function () { return this._y };
prototypeAccessors.z.set = function (value) {
this._z = value;
this.triggerEvent('valuechanged', {z: value});
};
prototypeAccessors.z.get = function () { return this._z };
Object.defineProperties( XYZValues.prototype, prototypeAccessors );
return XYZValues;
}(Observable));
function epsilon(value) {
return Math.abs(value) < 0.000001 ? 0 : value;
}
function applyCSSLabel(value, label) {
if (value === 0) {
return '0px'
} else if (label === '%') {
return value * 100 + '%';
} else if (label === 'px') {
return value + 'px'
}
}
function animationFrame() {
let resolve = null;
const promise = new Promise(r => resolve = r);
window.requestAnimationFrame(resolve);
return promise
}
// Create lowercase versions of each setter property.
// we care only about the setters, for now.
function makeLowercaseSetterAliases(object) {
const props = Object.getOwnPropertyNames(object);
for (let l=props.length, i=0; i<l; i+=1) {
const prop = props[i];
const lowercaseProp = prop.toLowerCase();
if (lowercaseProp != prop) {
const descriptor = Object.getOwnPropertyDescriptor(object, prop);
if (typeof descriptor.set != 'undefined') {
Object.defineProperty(object, lowercaseProp, descriptor);
}
}
}
}
let childObservationHandlers = null;
let childObserver = null;
function observeChildren(ctx, onConnect, onDisconnect) {
if (!childObservationHandlers) { childObservationHandlers = new Map; }
if (!childObserver) { childObserver = createChildObserver(); }
childObservationHandlers.set(ctx, {onConnect, onDisconnect});
childObserver.observe(ctx, { childList: true });
return true
}
// NOTE: If a child is disconnected then connected to the same parent in the
// same turn, then the onConnect and onDisconnect callbacks won't be called
// because the DOM tree will be back in the exact state as before (this is
// possible thanks to the logic associated with weightsPerTarget).
function createChildObserver() {
return new MutationObserver(changes => {
const weightsPerTarget = new Map;
// We're just counting how many times each child node was added and
// removed from the parent we're observing.
for (let i=0, l=changes.length; i<l; i+=1) {
const change = changes[i];
if (change.type != 'childList') { continue }
if (!weightsPerTarget.has(change.target))
{ weightsPerTarget.set(change.target, new Map); }
const weights = weightsPerTarget.get(change.target);
var addedNodes = change.addedNodes;
for (let l=addedNodes.length, i=0; i<l; i+=1)
{ weights.set(addedNodes[i], (weights.get(addedNodes[i]) || 0) + 1); }
var removedNodes = change.removedNodes;
for (let l=removedNodes.length, i=0; i<l; i+=1)
{ weights.set(removedNodes[i], (weights.get(removedNodes[i]) || 0) - 1); }
}
// TODO PERFORMANCE: Can these for..of loops be converted to regular for loops?
for (var i = 0, list = weightsPerTarget; i < list.length; i += 1) {
const ref = list[i];
var target = ref[0];
var weights = ref[1];
var ref$1 = childObservationHandlers.get(target);
var onConnect = ref$1.onConnect;
var onDisconnect = ref$1.onDisconnect;
for (var i$1 = 0, list$1 = weights; i$1 < list$1.length; i$1 += 1) {
// If the number of times a child was added is greater than the
// number of times it was removed, then the net result is that
// it was added, so we call onConnect just once.
const ref$2 = list$1[i$1];
var node = ref$2[0];
var weight = ref$2[1];
if (weight > 0 && typeof onConnect == 'function')
{ onConnect.call(target, node); }
// If the number of times a child was added is less than the
// number of times it was removed, then the net result is that
// it was removed, so we call onConnect just once.
else if (weight < 0 && typeof onDisconnect == 'function')
{ onDisconnect.call(target, node); }
// If the number of times a child was added is equal to the
// number of times it was removed, then it was essentially left
// in place, so we don't call anything.
}
}
})
}
const hasShadowDomV0 =
typeof Element.prototype.createShadowRoot == 'function'
&& typeof HTMLContentElement == 'function'
? true : false;
const hasShadowDomV1 =
typeof Element.prototype.attachShadow == 'function'
&& typeof HTMLSlotElement == 'function'
? true : false;
function getShadowRootVersion(shadowRoot) {
console.log('getShadowRootVersion');
if (!shadowRoot) { return null }
const slot = document.createElement('slot');
shadowRoot.appendChild(slot);
slot.appendChild(document.createElement('div'));
const assignedNodes = slot.assignedNodes({ flatten: true });
slot.remove();
console.log('hmm', assignedNodes.length, assignedNodes.length > 0 ? 'v1' : 'v0');
return assignedNodes.length > 0 ? 'v1' : 'v0'
}
function getAncestorShadowRoot(node) {
let current = node;
while (current && !(current instanceof ShadowRoot)) {
current = current.parentNode;
}
return current
}
// in the future, the user will be able to toggle the HTML API.
const hasHtmlApi = true;
// Traverses a tree while considering ShadowDOM disribution.
function traverse(node, isShadowChild) {
console.log(isShadowChild ? 'distributedNode:' : 'node:', node);
var children = node.children;
for (let l=children.length, i=0; i<l; i+=1) {
// skip nodes that are possiblyDistributed, i.e. they have a parent
// that has a ShadowRoot.
if (!hasHtmlApi || !children[i]._elementManager.element._isPossiblyDistributed)
{ traverse(children[i]); }
}
const shadowChildren = node._elementManager.element._shadowChildren;
if (hasHtmlApi && shadowChildren) {
for (let l=shadowChildren.length, i=0; i<l; i+=1)
{ traverse(shadowChildren[i].imperativeCounterpart, true); }
}
}
// helper function to use instead of instanceof for classes that implement the
// static Symbol.hasInstance method, because the behavior of instanceof isn't
// polyfillable.
function isInstanceof(lhs, rhs) {
if (typeof rhs == 'function' && rhs[Symbol.hasInstance])
{ return rhs[Symbol.hasInstance](lhs) }
else { return lhs instanceof rhs }
}
var Utility$2 = Object.freeze({
epsilon: epsilon,
applyCSSLabel: applyCSSLabel,
animationFrame: animationFrame,
makeLowercaseSetterAliases: makeLowercaseSetterAliases,
observeChildren: observeChildren,
getShadowRootVersion: getShadowRootVersion,
hasShadowDomV0: hasShadowDomV0,
hasShadowDomV1: hasShadowDomV1,
getAncestorShadowRoot: getAncestorShadowRoot,
traverse: traverse,
isInstanceof: isInstanceof
});
const instanceofSymbol$4 = Symbol('instanceofSymbol');
const TreeNodeMixin = base => {
var TreeNode = (function (base) {
function TreeNode(options) {
if ( options === void 0 ) options = {};
base.call(this, options);
this._parent = null; // default to no parent.
this._children = [];
}
if ( base ) TreeNode.__proto__ = base;
TreeNode.prototype = Object.create( base && base.prototype );
TreeNode.prototype.constructor = TreeNode;
var prototypeAccessors = { parent: {},children: {},childCount: {} };
/**
* this._parent is protected (node's can access other node._parent).
* The user should use the add() method, which automatically handles
* setting a parent.
*
* @readonly
*/
prototypeAccessors.parent.get = function () {
return this._parent
};
/**
* @readonly
*/
prototypeAccessors.children.get = function () {
// return a new array, so that the user modifying it doesn't affect
// this node's actual children.
return [...this._children]
};
/**
* Add a child node to this TreeNode.
*
* @param {TreeNode} childNode The child node to add.
*/
TreeNode.prototype.add = function add (childNode) {
if (! isInstanceof(childNode, TreeNode))
{ throw new TypeError('TreeNode.add() expects the childNode argument to be a TreeNode instance.') }
if (childNode._parent === this)
{ throw new ReferenceError('childNode is already a child of this parent.') }
if (childNode._parent)
{ childNode._parent.remove(childNode); }
childNode._parent = this;
this._children.push(childNode);
return this
};
/**
* Add all the child nodes in the given array to this node.
*
* @param {Array.TreeNode} nodes The nodes to add.
*/
TreeNode.prototype.addChildren = function addChildren (nodes) {
nodes.forEach(node => this.add()(node));
return this
};
/**
* Remove a child node from this node.
*
* @param {TreeNode} childNode The node to remove.
*/
TreeNode.prototype.remove = function remove (childNode) {
if (! isInstanceof(childNode, TreeNode))
{ throw new Error(`
TreeNode.remove expects the childNode argument to be an
instance of TreeNode. There should only be TreeNodes in the
tree.
`) }
if (childNode._parent !== this)
{ throw new ReferenceError('childNode is not a child of this parent.') }
childNode._parent = null;
this._children.splice(this._children.indexOf(childNode), 1);
return this
};
/**
* Remove all the child nodes in the given array from this node.
*
* @param {Array.TreeNode} nodes The nodes to remove.
*/
TreeNode.prototype.removeChildren = function removeChildren (nodes) {
nodes.forEach(node => this.remove(node));
return this
};
/**
* Shortcut to remove all children.
*/
TreeNode.prototype.removeAllChildren = function removeAllChildren () {
this.removeChildren(this._children);
return this
};
/**
* @readonly
* @return {number} How many children this TreeNode has.
*/
prototypeAccessors.childCount.get = function () {
return this._children.length
};
Object.defineProperties( TreeNode.prototype, prototypeAccessors );
return TreeNode;
}(base));
Object.defineProperty(TreeNode, Symbol.hasInstance, {
value: function(obj) {
if (this !== TreeNode) { return Object.getPrototypeOf(TreeNode)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol$4))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
TreeNode[instanceofSymbol$4] = true;
return TreeNode
};
const TreeNode = TreeNodeMixin((function () {
function anonymous () {}
return anonymous;
}()));
TreeNode.mixin = TreeNodeMixin;
var runtime = createCommonjsModule(function (module) {
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
!(function(global) {
"use strict";
var Op = Object.prototype;
var hasOwn = Op.hasOwnProperty;
var undefined; // More compressible than void 0.
var $Symbol = typeof Symbol === "function" ? Symbol : {};
var iteratorSymbol = $Symbol.iterator || "@@iterator";
var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
var inModule = 'object' === "object";
var runtime = global.regeneratorRuntime;
if (runtime) {
if (inModule) {
// If regeneratorRuntime is defined globally and we're in a module,
// make the exports object identical to regeneratorRuntime.
module.exports = runtime;
}
// Don't bother evaluating the rest of this file if the runtime was
// already defined globally.
return;
}
// Define the runtime globally (as expected by generated code) as either
// module.exports (if we're in a module) or a new, empty object.
runtime = global.regeneratorRuntime = inModule ? module.exports : {};
function wrap(innerFn, outerFn, self, tryLocsList) {
// If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
var generator = Object.create(protoGenerator.prototype);
var context = new Context(tryLocsList || []);
// The ._invoke method unifies the implementations of the .next,
// .throw, and .return methods.
generator._invoke = makeInvokeMethod(innerFn, self, context);
return generator;
}
runtime.wrap = wrap;
// Try/catch helper to minimize deoptimizations. Returns a completion
// record like context.tryEntries[i].completion. This interface could
// have been (and was previously) designed to take a closure to be
// invoked without arguments, but in all the cases we care about we
// already have an existing method we want to call, so there's no need
// to create a new function object. We can even get away with assuming
// the method takes exactly one argument, since that happens to be true
// in every case, so we don't have to touch the arguments object. The
// only additional allocation required is the completion record, which
// has a stable shape and so hopefully should be cheap to allocate.
function tryCatch(fn, obj, arg) {
try {
return { type: "normal", arg: fn.call(obj, arg) };
} catch (err) {
return { type: "throw", arg: err };
}
}
var GenStateSuspendedStart = "suspendedStart";
var GenStateSuspendedYield = "suspendedYield";
var GenStateExecuting = "executing";
var GenStateCompleted = "completed";
// Returning this object from the innerFn has the same effect as
// breaking out of the dispatch switch statement.
var ContinueSentinel = {};
// Dummy constructor functions that we use as the .constructor and
// .constructor.prototype properties for functions that return Generator
// objects. For full spec compliance, you may wish to configure your
// minifier not to mangle the names of these two functions.
function Generator() {}
function GeneratorFunction() {}
function GeneratorFunctionPrototype() {}
// This is a polyfill for %IteratorPrototype% for environments that
// don't natively support it.
var IteratorPrototype = {};
IteratorPrototype[iteratorSymbol] = function () {
return this;
};
var getProto = Object.getPrototypeOf;
var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
if (NativeIteratorPrototype &&
NativeIteratorPrototype !== Op &&
hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
// This environment has a native %IteratorPrototype%; use it instead
// of the polyfill.
IteratorPrototype = NativeIteratorPrototype;
}
var Gp = GeneratorFunctionPrototype.prototype =
Generator.prototype = Object.create(IteratorPrototype);
GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
GeneratorFunctionPrototype.constructor = GeneratorFunction;
GeneratorFunctionPrototype[toStringTagSymbol] =
GeneratorFunction.displayName = "GeneratorFunction";
// Helper for defining the .next, .throw, and .return methods of the
// Iterator interface in terms of a single ._invoke method.
function defineIteratorMethods(prototype) {
["next", "throw", "return"].forEach(function(method) {
prototype[method] = function(arg) {
return this._invoke(method, arg);
};
});
}
runtime.isGeneratorFunction = function(genFun) {
var ctor = typeof genFun === "function" && genFun.constructor;
return ctor
? ctor === GeneratorFunction ||
// For the native GeneratorFunction constructor, the best we can
// do is to check its .name property.
(ctor.displayName || ctor.name) === "GeneratorFunction"
: false;
};
runtime.mark = function(genFun) {
if (Object.setPrototypeOf) {
Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
} else {
genFun.__proto__ = GeneratorFunctionPrototype;
if (!(toStringTagSymbol in genFun)) {
genFun[toStringTagSymbol] = "GeneratorFunction";
}
}
genFun.prototype = Object.create(Gp);
return genFun;
};
// Within the body of any async function, `await x` is transformed to
// `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
// `hasOwn.call(value, "__await")` to determine if the yielded value is
// meant to be awaited.
runtime.awrap = function(arg) {
return { __await: arg };
};
function AsyncIterator(generator) {
function invoke(method, arg, resolve, reject) {
var record = tryCatch(generator[method], generator, arg);
if (record.type === "throw") {
reject(record.arg);
} else {
var result = record.arg;
var value = result.value;
if (value &&
typeof value === "object" &&
hasOwn.call(value, "__await")) {
return Promise.resolve(value.__await).then(function(value) {
invoke("next", value, resolve, reject);
}, function(err) {
invoke("throw", err, resolve, reject);
});
}
return Promise.resolve(value).then(function(unwrapped) {
// When a yielded Promise is resolved, its final value becomes
// the .value of the Promise<{value,done}> result for the
// current iteration. If the Promise is rejected, however, the
// result for this iteration will be rejected with the same
// reason. Note that rejections of yielded Promises are not
// thrown back into the generator function, as is the case
// when an awaited Promise is rejected. This difference in
// behavior between yield and await is important, because it
// allows the consumer to decide what to do with the yielded
// rejection (swallow it and continue, manually .throw it back
// into the generator, abandon iteration, whatever). With
// await, by contrast, there is no opportunity to examine the
// rejection reason outside the generator function, so the
// only option is to throw it from the await expression, and
// let the generator function handle the exception.
result.value = unwrapped;
resolve(result);
}, reject);
}
}
if (typeof global.process === "object" && global.process.domain) {
invoke = global.process.domain.bind(invoke);
}
var previousPromise;
function enqueue(method, arg) {
function callInvokeWithMethodAndArg() {
return new Promise(function(resolve, reject) {
invoke(method, arg, resolve, reject);
});
}
return previousPromise =
// If enqueue has been called before, then we want to wait until
// all previous Promises have been resolved before calling invoke,
// so that results are always delivered in the correct order. If
// enqueue has not been called before, then it is important to
// call invoke immediately, without waiting on a callback to fire,
// so that the async generator function has the opportunity to do
// any necessary setup in a predictable way. This predictability
// is why the Promise constructor synchronously invokes its
// executor callback, and why async functions synchronously
// execute code before the first await. Since we implement simple
// async functions in terms of async generators, it is especially
// important to get this right, even though it requires care.
previousPromise ? previousPromise.then(
callInvokeWithMethodAndArg,
// Avoid propagating failures to Promises returned by later
// invocations of the iterator.
callInvokeWithMethodAndArg
) : callInvokeWithMethodAndArg();
}
// Define the unified helper method that is used to implement .next,
// .throw, and .return (see defineIteratorMethods).
this._invoke = enqueue;
}
defineIteratorMethods(AsyncIterator.prototype);
AsyncIterator.prototype[asyncIteratorSymbol] = function () {
return this;
};
runtime.AsyncIterator = AsyncIterator;
// Note that simple async functions are implemented on top of
// AsyncIterator objects; they just return a Promise for the value of
// the final result produced by the iterator.
runtime.async = function(innerFn, outerFn, self, tryLocsList) {
var iter = new AsyncIterator(
wrap(innerFn, outerFn, self, tryLocsList)
);
return runtime.isGeneratorFunction(outerFn)
? iter // If outerFn is a generator, return the full iterator.
: iter.next().then(function(result) {
return result.done ? result.value : iter.next();
});
};
function makeInvokeMethod(innerFn, self, context) {
var state = GenStateSuspendedStart;
return function invoke(method, arg) {
if (state === GenStateExecuting) {
throw new Error("Generator is already running");
}
if (state === GenStateCompleted) {
if (method === "throw") {
throw arg;
}
// Be forgiving, per 25.3.3.3.3 of the spec:
// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
return doneResult();
}
context.method = method;
context.arg = arg;
while (true) {
var delegate = context.delegate;
if (delegate) {
var delegateResult = maybeInvokeDelegate(delegate, context);
if (delegateResult) {
if (delegateResult === ContinueSentinel) { continue; }
return delegateResult;
}
}
if (context.method === "next") {
// Setting context._sent for legacy support of Babel's
// function.sent implementation.
context.sent = context._sent = context.arg;
} else if (context.method === "throw") {
if (state === GenStateSuspendedStart) {
state = GenStateCompleted;
throw context.arg;
}
context.dispatchException(context.arg);
} else if (context.method === "return") {
context.abrupt("return", context.arg);
}
state = GenStateExecuting;
var record = tryCatch(innerFn, self, context);
if (record.type === "normal") {
// If an exception is thrown from innerFn, we leave state ===
// GenStateExecuting and loop back for another invocation.
state = context.done
? GenStateCompleted
: GenStateSuspendedYield;
if (record.arg === ContinueSentinel) {
continue;
}
return {
value: record.arg,
done: context.done
};
} else if (record.type === "throw") {
state = GenStateCompleted;
// Dispatch the exception by looping back around to the
// context.dispatchException(context.arg) call above.
context.method = "throw";
context.arg = record.arg;
}
}
};
}
// Call delegate.iterator[context.method](context.arg) and handle the
// result, either by returning a { value, done } result from the
// delegate iterator, or by modifying context.method and context.arg,
// setting context.delegate to null, and returning the ContinueSentinel.
function maybeInvokeDelegate(delegate, context) {
var method = delegate.iterator[context.method];
if (method === undefined) {
// A .throw or .return when the delegate iterator has no .throw
// method always terminates the yield* loop.
context.delegate = null;
if (context.method === "throw") {
if (delegate.iterator.return) {
// If the delegate iterator has a return method, give it a
// chance to clean up.
context.method = "return";
context.arg = undefined;
maybeInvokeDelegate(delegate, context);
if (context.method === "throw") {
// If maybeInvokeDelegate(context) changed context.method from
// "return" to "throw", let that override the TypeError below.
return ContinueSentinel;
}
}
context.method = "throw";
context.arg = new TypeError(
"The iterator does not provide a 'throw' method");
}
return ContinueSentinel;
}
var record = tryCatch(method, delegate.iterator, context.arg);
if (record.type === "throw") {
context.method = "throw";
context.arg = record.arg;
context.delegate = null;
return ContinueSentinel;
}
var info = record.arg;
if (! info) {
context.method = "throw";
context.arg = new TypeError("iterator result is not an object");
context.delegate = null;
return ContinueSentinel;
}
if (info.done) {
// Assign the result of the finished delegate to the temporary
// variable specified by delegate.resultName (see delegateYield).
context[delegate.resultName] = info.value;
// Resume execution at the desired location (see delegateYield).
context.next = delegate.nextLoc;
// If context.method was "throw" but the delegate handled the
// exception, let the outer generator proceed normally. If
// context.method was "next", forget context.arg since it has been
// "consumed" by the delegate iterator. If context.method was
// "return", allow the original .return call to continue in the
// outer generator.
if (context.method !== "return") {
context.method = "next";
context.arg = undefined;
}
} else {
// Re-yield the result returned by the delegate method.
return info;
}
// The delegate iterator is finished, so forget it and continue with
// the outer generator.
context.delegate = null;
return ContinueSentinel;
}
// Define Generator.prototype.{next,throw,return} in terms of the
// unified ._invoke helper method.
defineIteratorMethods(Gp);
Gp[toStringTagSymbol] = "Generator";
// A Generator should always return itself as the iterator object when the
// @@iterator function is called on it. Some browsers' implementations of the
// iterator prototype chain incorrectly implement this, causing the Generator
// object to not be returned from this call. This ensures that doesn't happen.
// See https://github.com/facebook/regenerator/issues/274 for more details.
Gp[iteratorSymbol] = function() {
return this;
};
Gp.toString = function() {
return "[object Generator]";
};
function pushTryEntry(locs) {
var entry = { tryLoc: locs[0] };
if (1 in locs) {
entry.catchLoc = locs[1];
}
if (2 in locs) {
entry.finallyLoc = locs[2];
entry.afterLoc = locs[3];
}
this.tryEntries.push(entry);
}
function resetTryEntry(entry) {
var record = entry.completion || {};
record.type = "normal";
delete record.arg;
entry.completion = record;
}
function Context(tryLocsList) {
// The root entry object (effectively a try statement without a catch
// or a finally block) gives us a place to store values thrown from
// locations where there is no enclosing try statement.
this.tryEntries = [{ tryLoc: "root" }];
tryLocsList.forEach(pushTryEntry, this);
this.reset(true);
}
runtime.keys = function(object) {
var keys = [];
for (var key in object) {
keys.push(key);
}
keys.reverse();
// Rather than returning an object with a next method, we keep
// things simple and return the next function itself.
return function next() {
while (keys.length) {
var key = keys.pop();
if (key in object) {
next.value = key;
next.done = false;
return next;
}
}
// To avoid creating an additional object, we just hang the .value
// and .done properties off the next function object itself. This
// also ensures that the minifier will not anonymize the function.
next.done = true;
return next;
};
};
function values(iterable) {
if (iterable) {
var iteratorMethod = iterable[iteratorSymbol];
if (iteratorMethod) {
return iteratorMethod.call(iterable);
}
if (typeof iterable.next === "function") {
return iterable;
}
if (!isNaN(iterable.length)) {
var i = -1, next = function next() {
while (++i < iterable.length) {
if (hasOwn.call(iterable, i)) {
next.value = iterable[i];
next.done = false;
return next;
}
}
next.value = undefined;
next.done = true;
return next;
};
return next.next = next;
}
}
// Return an iterator with no values.
return { next: doneResult };
}
runtime.values = values;
function doneResult() {
return { value: undefined, done: true };
}
Context.prototype = {
constructor: Context,
reset: function(skipTempReset) {
this.prev = 0;
this.next = 0;
// Resetting context._sent for legacy support of Babel's
// function.sent implementation.
this.sent = this._sent = undefined;
this.done = false;
this.delegate = null;
this.method = "next";
this.arg = undefined;
this.tryEntries.forEach(resetTryEntry);
if (!skipTempReset) {
for (var name in this) {
// Not sure about the optimal order of these conditions:
if (name.charAt(0) === "t" &&
hasOwn.call(this, name) &&
!isNaN(+name.slice(1))) {
this[name] = undefined;
}
}
}
},
stop: function() {
this.done = true;
var rootEntry = this.tryEntries[0];
var rootRecord = rootEntry.completion;
if (rootRecord.type === "throw") {
throw rootRecord.arg;
}
return this.rval;
},
dispatchException: function(exception) {
if (this.done) {
throw exception;
}
var context = this;
function handle(loc, caught) {
record.type = "throw";
record.arg = exception;
context.next = loc;
if (caught) {
// If the dispatched exception was caught by a catch block,
// then let that catch block handle the exception normally.
context.method = "next";
context.arg = undefined;
}
return !! caught;
}
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
var record = entry.completion;
if (entry.tryLoc === "root") {
// Exception thrown outside of any try block that could handle
// it, so set the completion value of the entire function to
// throw the exception.
return handle("end");
}
if (entry.tryLoc <= this.prev) {
var hasCatch = hasOwn.call(entry, "catchLoc");
var hasFinally = hasOwn.call(entry, "finallyLoc");
if (hasCatch && hasFinally) {
if (this.prev < entry.catchLoc) {
return handle(entry.catchLoc, true);
} else if (this.prev < entry.finallyLoc) {
return handle(entry.finallyLoc);
}
} else if (hasCatch) {
if (this.prev < entry.catchLoc) {
return handle(entry.catchLoc, true);
}
} else if (hasFinally) {
if (this.prev < entry.finallyLoc) {
return handle(entry.finallyLoc);
}
} else {
throw new Error("try statement without catch or finally");
}
}
}
},
abrupt: function(type, arg) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc <= this.prev &&
hasOwn.call(entry, "finallyLoc") &&
this.prev < entry.finallyLoc) {
var finallyEntry = entry;
break;
}
}
if (finallyEntry &&
(type === "break" ||
type === "continue") &&
finallyEntry.tryLoc <= arg &&
arg <= finallyEntry.finallyLoc) {
// Ignore the finally entry if control is not jumping to a
// location outside the try/catch block.
finallyEntry = null;
}
var record = finallyEntry ? finallyEntry.completion : {};
record.type = type;
record.arg = arg;
if (finallyEntry) {
this.method = "next";
this.next = finallyEntry.finallyLoc;
return ContinueSentinel;
}
return this.complete(record);
},
complete: function(record, afterLoc) {
if (record.type === "throw") {
throw record.arg;
}
if (record.type === "break" ||
record.type === "continue") {
this.next = record.arg;
} else if (record.type === "return") {
this.rval = this.arg = record.arg;
this.method = "return";
this.next = "end";
} else if (record.type === "normal" && afterLoc) {
this.next = afterLoc;
}
return ContinueSentinel;
},
finish: function(finallyLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.finallyLoc === finallyLoc) {
this.complete(entry.completion, entry.afterLoc);
resetTryEntry(entry);
return ContinueSentinel;
}
}
},
"catch": function(tryLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc === tryLoc) {
var record = entry.completion;
if (record.type === "throw") {
var thrown = record.arg;
resetTryEntry(entry);
}
return thrown;
}
}
// The context.catch method must only be called with a location
// argument that corresponds to a known catch block.
throw new Error("illegal catch attempt");
},
delegateYield: function(iterable, resultName, nextLoc) {
this.delegate = {
iterator: values(iterable),
resultName: resultName,
nextLoc: nextLoc
};
if (this.method === "next") {
// Deliberately forget the last sent value so that we don't
// accidentally pass it on to the delegate.
this.arg = undefined;
}
return ContinueSentinel;
}
};
})(
// Among the various tricks for obtaining a reference to the global
// object, this seems to be the most reliable technique that does not
// use indirect eval (which violates Content Security Policy).
typeof commonjsGlobal === "object" ? commonjsGlobal :
typeof window === "object" ? window :
typeof self === "object" ? self : commonjsGlobal
);
});
// This method of obtaining a reference to the global object needs to be
// kept identical to the way it is obtained in runtime.js
var g =
typeof commonjsGlobal === "object" ? commonjsGlobal :
typeof window === "object" ? window :
typeof self === "object" ? self : commonjsGlobal;
// Use `getOwnPropertyNames` because not all browsers support calling
// `hasOwnProperty` on the global `self` object in a worker. See #183.
var hadRuntime = g.regeneratorRuntime &&
Object.getOwnPropertyNames(g).indexOf("regeneratorRuntime") >= 0;
// Save the old regeneratorRuntime in case it needs to be restored later.
var oldRuntime = hadRuntime && g.regeneratorRuntime;
// Force reevalutation of runtime.js.
g.regeneratorRuntime = undefined;
var runtimeModule = runtime;
if (hadRuntime) {
// Restore the original runtime.
g.regeneratorRuntime = oldRuntime;
} else {
// Remove the global property added by runtime.js.
try {
delete g.regeneratorRuntime;
} catch(e) {
g.regeneratorRuntime = undefined;
}
}
var index$10 = runtimeModule;
// 7.1.4 ToInteger
var ceil = Math.ceil;
var floor = Math.floor;
var _toInteger = function (it) {
return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
};
// 7.2.1 RequireObjectCoercible(argument)
var _defined = function (it) {
if (it == undefined) { throw TypeError("Can't call method on " + it); }
return it;
};
// true -> String#at
// false -> String#codePointAt
var _stringAt = function (TO_STRING) {
return function (that, pos) {
var s = String(_defined(that));
var i = _toInteger(pos);
var l = s.length;
var a, b;
if (i < 0 || i >= l) { return TO_STRING ? '' : undefined; }
a = s.charCodeAt(i);
return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
? TO_STRING ? s.charAt(i) : a
: TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
};
};
var _library = true;
var _global$1 = createCommonjsModule(function (module) {
// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
var global = module.exports = typeof window != 'undefined' && window.Math == Math
? window : typeof self != 'undefined' && self.Math == Math ? self
// eslint-disable-next-line no-new-func
: Function('return this')();
if (typeof __g == 'number') { __g = global; } // eslint-disable-line no-undef
});
var _core = createCommonjsModule(function (module) {
var core = module.exports = { version: '2.5.0' };
if (typeof __e == 'number') { __e = core; } // eslint-disable-line no-undef
});
var _aFunction = function (it) {
if (typeof it != 'function') { throw TypeError(it + ' is not a function!'); }
return it;
};
// optional / simple context binding
var _ctx = function (fn, that, length) {
_aFunction(fn);
if (that === undefined) { return fn; }
switch (length) {
case 1: return function (a) {
return fn.call(that, a);
};
case 2: return function (a, b) {
return fn.call(that, a, b);
};
case 3: return function (a, b, c) {
return fn.call(that, a, b, c);
};
}
return function (/* ...args */) {
return fn.apply(that, arguments);
};
};
var _isObject = function (it) {
return typeof it === 'object' ? it !== null : typeof it === 'function';
};
var _anObject = function (it) {
if (!_isObject(it)) { throw TypeError(it + ' is not an object!'); }
return it;
};
var _fails = function (exec) {
try {
return !!exec();
} catch (e) {
return true;
}
};
// Thank's IE8 for his funny defineProperty
var _descriptors = !_fails(function () {
return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
});
var document$1 = _global$1.document;
// typeof document.createElement is 'object' in old IE
var is = _isObject(document$1) && _isObject(document$1.createElement);
var _domCreate = function (it) {
return is ? document$1.createElement(it) : {};
};
var _ie8DomDefine = !_descriptors && !_fails(function () {
return Object.defineProperty(_domCreate('div'), 'a', { get: function () { return 7; } }).a != 7;
});
// 7.1.1 ToPrimitive(input [, PreferredType])
// instead of the ES6 spec version, we didn't implement @@toPrimitive case
// and the second argument - flag - preferred type is a string
var _toPrimitive = function (it, S) {
if (!_isObject(it)) { return it; }
var fn, val;
if (S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) { return val; }
if (typeof (fn = it.valueOf) == 'function' && !_isObject(val = fn.call(it))) { return val; }
if (!S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) { return val; }
throw TypeError("Can't convert object to primitive value");
};
var dP = Object.defineProperty;
var f = _descriptors ? Object.defineProperty : function defineProperty(O, P, Attributes) {
_anObject(O);
P = _toPrimitive(P, true);
_anObject(Attributes);
if (_ie8DomDefine) { try {
return dP(O, P, Attributes);
} catch (e) { /* empty */ } }
if ('get' in Attributes || 'set' in Attributes) { throw TypeError('Accessors not supported!'); }
if ('value' in Attributes) { O[P] = Attributes.value; }
return O;
};
var _objectDp = {
f: f
};
var _propertyDesc = function (bitmap, value) {
return {
enumerable: !(bitmap & 1),
configurable: !(bitmap & 2),
writable: !(bitmap & 4),
value: value
};
};
var _hide = _descriptors ? function (object, key, value) {
return _objectDp.f(object, key, _propertyDesc(1, value));
} : function (object, key, value) {
object[key] = value;
return object;
};
var PROTOTYPE = 'prototype';
var $export = function (type, name, source) {
var IS_FORCED = type & $export.F;
var IS_GLOBAL = type & $export.G;
var IS_STATIC = type & $export.S;
var IS_PROTO = type & $export.P;
var IS_BIND = type & $export.B;
var IS_WRAP = type & $export.W;
var exports = IS_GLOBAL ? _core : _core[name] || (_core[name] = {});
var expProto = exports[PROTOTYPE];
var target = IS_GLOBAL ? _global$1 : IS_STATIC ? _global$1[name] : (_global$1[name] || {})[PROTOTYPE];
var key, own, out;
if (IS_GLOBAL) { source = name; }
for (key in source) {
// contains in native
own = !IS_FORCED && target && target[key] !== undefined;
if (own && key in exports) { continue; }
// export native or passed
out = own ? target[key] : source[key];
// prevent global pollution for namespaces
exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
// bind timers to global for call from export context
: IS_BIND && own ? _ctx(out, _global$1)
// wrap global constructors for prevent change them in library
: IS_WRAP && target[key] == out ? (function (C) {
var F = function (a, b, c) {
if (this instanceof C) {
switch (arguments.length) {
case 0: return new C();
case 1: return new C(a);
case 2: return new C(a, b);
} return new C(a, b, c);
} return C.apply(this, arguments);
};
F[PROTOTYPE] = C[PROTOTYPE];
return F;
// make static versions for prototype methods
})(out) : IS_PROTO && typeof out == 'function' ? _ctx(Function.call, out) : out;
// export proto methods to core.%CONSTRUCTOR%.methods.%NAME%
if (IS_PROTO) {
(exports.virtual || (exports.virtual = {}))[key] = out;
// export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%
if (type & $export.R && expProto && !expProto[key]) { _hide(expProto, key, out); }
}
}
};
// type bitmap
$export.F = 1; // forced
$export.G = 2; // global
$export.S = 4; // static
$export.P = 8; // proto
$export.B = 16; // bind
$export.W = 32; // wrap
$export.U = 64; // safe
$export.R = 128; // real proto method for `library`
var _export = $export;
var _redefine = _hide;
var hasOwnProperty = {}.hasOwnProperty;
var _has = function (it, key) {
return hasOwnProperty.call(it, key);
};
var _iterators = {};
var toString = {}.toString;
var _cof = function (it) {
return toString.call(it).slice(8, -1);
};
// fallback for non-array-like ES3 and non-enumerable old V8 strings
// eslint-disable-next-line no-prototype-builtins
var _iobject = Object('z').propertyIsEnumerable(0) ? Object : function (it) {
return _cof(it) == 'String' ? it.split('') : Object(it);
};
// to indexed object, toObject with fallback for non-array-like ES3 strings
var _toIobject = function (it) {
return _iobject(_defined(it));
};
// 7.1.15 ToLength
var min = Math.min;
var _toLength = function (it) {
return it > 0 ? min(_toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991
};
var max = Math.max;
var min$1 = Math.min;
var _toAbsoluteIndex = function (index, length) {
index = _toInteger(index);
return index < 0 ? max(index + length, 0) : min$1(index, length);
};
// false -> Array#indexOf
// true -> Array#includes
var _arrayIncludes = function (IS_INCLUDES) {
return function ($this, el, fromIndex) {
var O = _toIobject($this);
var length = _toLength(O.length);
var index = _toAbsoluteIndex(fromIndex, length);
var value;
// Array#includes uses SameValueZero equality algorithm
// eslint-disable-next-line no-self-compare
if (IS_INCLUDES && el != el) { while (length > index) {
value = O[index++];
// eslint-disable-next-line no-self-compare
if (value != value) { return true; }
// Array#indexOf ignores holes, Array#includes - not
} } else { for (;length > index; index++) { if (IS_INCLUDES || index in O) {
if (O[index] === el) { return IS_INCLUDES || index || 0; }
} } } return !IS_INCLUDES && -1;
};
};
var SHARED = '__core-js_shared__';
var store = _global$1[SHARED] || (_global$1[SHARED] = {});
var _shared = function (key) {
return store[key] || (store[key] = {});
};
var id = 0;
var px = Math.random();
var _uid = function (key) {
return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));
};
var shared = _shared('keys');
var _sharedKey = function (key) {
return shared[key] || (shared[key] = _uid(key));
};
var arrayIndexOf = _arrayIncludes(false);
var IE_PROTO$1 = _sharedKey('IE_PROTO');
var _objectKeysInternal = function (object, names) {
var O = _toIobject(object);
var i = 0;
var result = [];
var key;
for (key in O) { if (key != IE_PROTO$1) { _has(O, key) && result.push(key); } }
// Don't enum bug & hidden keys
while (names.length > i) { if (_has(O, key = names[i++])) {
~arrayIndexOf(result, key) || result.push(key);
} }
return result;
};
// IE 8- don't enum bug keys
var _enumBugKeys = (
'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'
).split(',');
// 19.1.2.14 / 15.2.3.14 Object.keys(O)
var _objectKeys = Object.keys || function keys(O) {
return _objectKeysInternal(O, _enumBugKeys);
};
var _objectDps = _descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
_anObject(O);
var keys = _objectKeys(Properties);
var length = keys.length;
var i = 0;
var P;
while (length > i) { _objectDp.f(O, P = keys[i++], Properties[P]); }
return O;
};
var document$2 = _global$1.document;
var _html = document$2 && document$2.documentElement;
// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
var IE_PROTO = _sharedKey('IE_PROTO');
var Empty = function () { /* empty */ };
var PROTOTYPE$1 = 'prototype';
// Create object with fake `null` prototype: use iframe Object with cleared prototype
var createDict = function () {
// Thrash, waste and sodomy: IE GC bug
var iframe = _domCreate('iframe');
var i = _enumBugKeys.length;
var lt = '<';
var gt = '>';
var iframeDocument;
iframe.style.display = 'none';
_html.appendChild(iframe);
iframe.src = 'javascript:'; // eslint-disable-line no-script-url
// createDict = iframe.contentWindow.Object;
// html.removeChild(iframe);
iframeDocument = iframe.contentWindow.document;
iframeDocument.open();
iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);
iframeDocument.close();
createDict = iframeDocument.F;
while (i--) { delete createDict[PROTOTYPE$1][_enumBugKeys[i]]; }
return createDict();
};
var _objectCreate = Object.create || function create(O, Properties) {
var result;
if (O !== null) {
Empty[PROTOTYPE$1] = _anObject(O);
result = new Empty();
Empty[PROTOTYPE$1] = null;
// add "__proto__" for Object.getPrototypeOf polyfill
result[IE_PROTO] = O;
} else { result = createDict(); }
return Properties === undefined ? result : _objectDps(result, Properties);
};
var _wks = createCommonjsModule(function (module) {
var store = _shared('wks');
var Symbol = _global$1.Symbol;
var USE_SYMBOL = typeof Symbol == 'function';
var $exports = module.exports = function (name) {
return store[name] || (store[name] =
USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : _uid)('Symbol.' + name));
};
$exports.store = store;
});
var def = _objectDp.f;
var TAG = _wks('toStringTag');
var _setToStringTag = function (it, tag, stat) {
if (it && !_has(it = stat ? it : it.prototype, TAG)) { def(it, TAG, { configurable: true, value: tag }); }
};
var IteratorPrototype = {};
// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
_hide(IteratorPrototype, _wks('iterator'), function () { return this; });
var _iterCreate = function (Constructor, NAME, next) {
Constructor.prototype = _objectCreate(IteratorPrototype, { next: _propertyDesc(1, next) });
_setToStringTag(Constructor, NAME + ' Iterator');
};
// 7.1.13 ToObject(argument)
var _toObject = function (it) {
return Object(_defined(it));
};
// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)
var IE_PROTO$2 = _sharedKey('IE_PROTO');
var ObjectProto = Object.prototype;
var _objectGpo = Object.getPrototypeOf || function (O) {
O = _toObject(O);
if (_has(O, IE_PROTO$2)) { return O[IE_PROTO$2]; }
if (typeof O.constructor == 'function' && O instanceof O.constructor) {
return O.constructor.prototype;
} return O instanceof Object ? ObjectProto : null;
};
var ITERATOR = _wks('iterator');
var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`
var FF_ITERATOR = '@@iterator';
var KEYS = 'keys';
var VALUES = 'values';
var returnThis = function () { return this; };
var _iterDefine = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {
_iterCreate(Constructor, NAME, next);
var getMethod = function (kind) {
if (!BUGGY && kind in proto) { return proto[kind]; }
switch (kind) {
case KEYS: return function keys() { return new Constructor(this, kind); };
case VALUES: return function values() { return new Constructor(this, kind); };
} return function entries() { return new Constructor(this, kind); };
};
var TAG = NAME + ' Iterator';
var DEF_VALUES = DEFAULT == VALUES;
var VALUES_BUG = false;
var proto = Base.prototype;
var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
var $default = $native || getMethod(DEFAULT);
var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
var methods, key, IteratorPrototype;
// Fix native
if ($anyNative) {
IteratorPrototype = _objectGpo($anyNative.call(new Base()));
if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {
// Set @@toStringTag to native iterators
_setToStringTag(IteratorPrototype, TAG, true);
// fix for some old engines
if (!_library && !_has(IteratorPrototype, ITERATOR)) { _hide(IteratorPrototype, ITERATOR, returnThis); }
}
}
// fix Array#{values, @@iterator}.name in V8 / FF
if (DEF_VALUES && $native && $native.name !== VALUES) {
VALUES_BUG = true;
$default = function values() { return $native.call(this); };
}
// Define iterator
if ((!_library || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {
_hide(proto, ITERATOR, $default);
}
// Plug for library
_iterators[NAME] = $default;
_iterators[TAG] = returnThis;
if (DEFAULT) {
methods = {
values: DEF_VALUES ? $default : getMethod(VALUES),
keys: IS_SET ? $default : getMethod(KEYS),
entries: $entries
};
if (FORCED) { for (key in methods) {
if (!(key in proto)) { _redefine(proto, key, methods[key]); }
} } else { _export(_export.P + _export.F * (BUGGY || VALUES_BUG), NAME, methods); }
}
return methods;
};
var $at = _stringAt(true);
// 21.1.3.27 String.prototype[@@iterator]()
_iterDefine(String, 'String', function (iterated) {
this._t = String(iterated); // target
this._i = 0; // next index
// 21.1.5.2.1 %StringIteratorPrototype%.next()
}, function () {
var O = this._t;
var index = this._i;
var point;
if (index >= O.length) { return { value: undefined, done: true }; }
point = $at(O, index);
this._i += point.length;
return { value: point, done: false };
});
var _addToUnscopables = function () { /* empty */ };
var _iterStep = function (done, value) {
return { value: value, done: !!done };
};
// 22.1.3.4 Array.prototype.entries()
// 22.1.3.13 Array.prototype.keys()
// 22.1.3.29 Array.prototype.values()
// 22.1.3.30 Array.prototype[@@iterator]()
var es6_array_iterator = _iterDefine(Array, 'Array', function (iterated, kind) {
this._t = _toIobject(iterated); // target
this._i = 0; // next index
this._k = kind; // kind
// 22.1.5.2.1 %ArrayIteratorPrototype%.next()
}, function () {
var O = this._t;
var kind = this._k;
var index = this._i++;
if (!O || index >= O.length) {
this._t = undefined;
return _iterStep(1);
}
if (kind == 'keys') { return _iterStep(0, index); }
if (kind == 'values') { return _iterStep(0, O[index]); }
return _iterStep(0, [index, O[index]]);
}, 'values');
// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)
_iterators.Arguments = _iterators.Array;
_addToUnscopables('keys');
_addToUnscopables('values');
_addToUnscopables('entries');
var TO_STRING_TAG = _wks('toStringTag');
var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' +
'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' +
'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' +
'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' +
'TextTrackList,TouchList').split(',');
for (var i = 0; i < DOMIterables.length; i++) {
var NAME = DOMIterables[i];
var Collection = _global$1[NAME];
var proto = Collection && Collection.prototype;
if (proto && !proto[TO_STRING_TAG]) { _hide(proto, TO_STRING_TAG, NAME); }
_iterators[NAME] = _iterators.Array;
}
// getting tag from 19.1.3.6 Object.prototype.toString()
var TAG$1 = _wks('toStringTag');
// ES3 wrong here
var ARG = _cof(function () { return arguments; }()) == 'Arguments';
// fallback for IE11 Script Access Denied error
var tryGet = function (it, key) {
try {
return it[key];
} catch (e) { /* empty */ }
};
var _classof = function (it) {
var O, T, B;
return it === undefined ? 'Undefined' : it === null ? 'Null'
// @@toStringTag case
: typeof (T = tryGet(O = Object(it), TAG$1)) == 'string' ? T
// builtinTag case
: ARG ? _cof(O)
// ES3 arguments fallback
: (B = _cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;
};
var _anInstance = function (it, Constructor, name, forbiddenField) {
if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) {
throw TypeError(name + ': incorrect invocation!');
} return it;
};
// call something on iterator step with safe closing on error
var _iterCall = function (iterator, fn, value, entries) {
try {
return entries ? fn(_anObject(value)[0], value[1]) : fn(value);
// 7.4.6 IteratorClose(iterator, completion)
} catch (e) {
var ret = iterator['return'];
if (ret !== undefined) { _anObject(ret.call(iterator)); }
throw e;
}
};
// check on default Array iterator
var ITERATOR$1 = _wks('iterator');
var ArrayProto = Array.prototype;
var _isArrayIter = function (it) {
return it !== undefined && (_iterators.Array === it || ArrayProto[ITERATOR$1] === it);
};
var ITERATOR$2 = _wks('iterator');
var core_getIteratorMethod = _core.getIteratorMethod = function (it) {
if (it != undefined) { return it[ITERATOR$2]
|| it['@@iterator']
|| _iterators[_classof(it)]; }
};
var _forOf = createCommonjsModule(function (module) {
var BREAK = {};
var RETURN = {};
var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) {
var iterFn = ITERATOR ? function () { return iterable; } : core_getIteratorMethod(iterable);
var f = _ctx(fn, that, entries ? 2 : 1);
var index = 0;
var length, step, iterator, result;
if (typeof iterFn != 'function') { throw TypeError(iterable + ' is not iterable!'); }
// fast case for arrays with default iterator
if (_isArrayIter(iterFn)) { for (length = _toLength(iterable.length); length > index; index++) {
result = entries ? f(_anObject(step = iterable[index])[0], step[1]) : f(iterable[index]);
if (result === BREAK || result === RETURN) { return result; }
} } else { for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) {
result = _iterCall(iterator, f, step.value, entries);
if (result === BREAK || result === RETURN) { return result; }
} }
};
exports.BREAK = BREAK;
exports.RETURN = RETURN;
});
// 7.3.20 SpeciesConstructor(O, defaultConstructor)
var SPECIES = _wks('species');
var _speciesConstructor = function (O, D) {
var C = _anObject(O).constructor;
var S;
return C === undefined || (S = _anObject(C)[SPECIES]) == undefined ? D : _aFunction(S);
};
// fast apply, http://jsperf.lnkit.com/fast-apply/5
var _invoke = function (fn, args, that) {
var un = that === undefined;
switch (args.length) {
case 0: return un ? fn()
: fn.call(that);
case 1: return un ? fn(args[0])
: fn.call(that, args[0]);
case 2: return un ? fn(args[0], args[1])
: fn.call(that, args[0], args[1]);
case 3: return un ? fn(args[0], args[1], args[2])
: fn.call(that, args[0], args[1], args[2]);
case 4: return un ? fn(args[0], args[1], args[2], args[3])
: fn.call(that, args[0], args[1], args[2], args[3]);
} return fn.apply(that, args);
};
var process$1 = _global$1.process;
var setTask = _global$1.setImmediate;
var clearTask = _global$1.clearImmediate;
var MessageChannel = _global$1.MessageChannel;
var Dispatch = _global$1.Dispatch;
var counter = 0;
var queue = {};
var ONREADYSTATECHANGE = 'onreadystatechange';
var defer;
var channel;
var port;
var run = function () {
var id = +this;
// eslint-disable-next-line no-prototype-builtins
if (queue.hasOwnProperty(id)) {
var fn = queue[id];
delete queue[id];
fn();
}
};
var listener = function (event) {
run.call(event.data);
};
// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
if (!setTask || !clearTask) {
setTask = function setImmediate(fn) {
var args = [];
var i = 1;
while (arguments.length > i) { args.push(arguments[i++]); }
queue[++counter] = function () {
// eslint-disable-next-line no-new-func
_invoke(typeof fn == 'function' ? fn : Function(fn), args);
};
defer(counter);
return counter;
};
clearTask = function clearImmediate(id) {
delete queue[id];
};
// Node.js 0.8-
if (_cof(process$1) == 'process') {
defer = function (id) {
process$1.nextTick(_ctx(run, id, 1));
};
// Sphere (JS game engine) Dispatch API
} else if (Dispatch && Dispatch.now) {
defer = function (id) {
Dispatch.now(_ctx(run, id, 1));
};
// Browsers with MessageChannel, includes WebWorkers
} else if (MessageChannel) {
channel = new MessageChannel();
port = channel.port2;
channel.port1.onmessage = listener;
defer = _ctx(port.postMessage, port, 1);
// Browsers with postMessage, skip WebWorkers
// IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
} else if (_global$1.addEventListener && typeof postMessage == 'function' && !_global$1.importScripts) {
defer = function (id) {
_global$1.postMessage(id + '', '*');
};
_global$1.addEventListener('message', listener, false);
// IE8-
} else if (ONREADYSTATECHANGE in _domCreate('script')) {
defer = function (id) {
_html.appendChild(_domCreate('script'))[ONREADYSTATECHANGE] = function () {
_html.removeChild(this);
run.call(id);
};
};
// Rest old browsers
} else {
defer = function (id) {
setTimeout(_ctx(run, id, 1), 0);
};
}
}
var _task = {
set: setTask,
clear: clearTask
};
var macrotask = _task.set;
var Observer = _global$1.MutationObserver || _global$1.WebKitMutationObserver;
var process$2 = _global$1.process;
var Promise$1 = _global$1.Promise;
var isNode$1 = _cof(process$2) == 'process';
var _microtask = function () {
var head, last, notify;
var flush = function () {
var parent, fn;
if (isNode$1 && (parent = process$2.domain)) { parent.exit(); }
while (head) {
fn = head.fn;
head = head.next;
try {
fn();
} catch (e) {
if (head) { notify(); }
else { last = undefined; }
throw e;
}
} last = undefined;
if (parent) { parent.enter(); }
};
// Node.js
if (isNode$1) {
notify = function () {
process$2.nextTick(flush);
};
// browsers with MutationObserver
} else if (Observer) {
var toggle = true;
var node = document.createTextNode('');
new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new
notify = function () {
node.data = toggle = !toggle;
};
// environments with maybe non-completely correct, but existent Promise
} else if (Promise$1 && Promise$1.resolve) {
var promise = Promise$1.resolve();
notify = function () {
promise.then(flush);
};
// for other environments - macrotask based on:
// - setImmediate
// - MessageChannel
// - window.postMessag
// - onreadystatechange
// - setTimeout
} else {
notify = function () {
// strange IE + webpack dev server bug - use .call(global)
macrotask.call(_global$1, flush);
};
}
return function (fn) {
var task = { fn: fn, next: undefined };
if (last) { last.next = task; }
if (!head) {
head = task;
notify();
} last = task;
};
};
// 25.4.1.5 NewPromiseCapability(C)
function PromiseCapability(C) {
var resolve, reject;
this.promise = new C(function ($$resolve, $$reject) {
if (resolve !== undefined || reject !== undefined) { throw TypeError('Bad Promise constructor'); }
resolve = $$resolve;
reject = $$reject;
});
this.resolve = _aFunction(resolve);
this.reject = _aFunction(reject);
}
var f$1 = function (C) {
return new PromiseCapability(C);
};
var _newPromiseCapability = {
f: f$1
};
var _perform = function (exec) {
try {
return { e: false, v: exec() };
} catch (e) {
return { e: true, v: e };
}
};
var _promiseResolve = function (C, x) {
var promiseCapability = _newPromiseCapability.f(C);
var resolve = promiseCapability.resolve;
resolve(x);
return promiseCapability.promise;
};
var _redefineAll = function (target, src, safe) {
for (var key in src) {
if (safe && target[key]) { target[key] = src[key]; }
else { _hide(target, key, src[key]); }
} return target;
};
var SPECIES$1 = _wks('species');
var _setSpecies = function (KEY) {
var C = typeof _core[KEY] == 'function' ? _core[KEY] : _global$1[KEY];
if (_descriptors && C && !C[SPECIES$1]) { _objectDp.f(C, SPECIES$1, {
configurable: true,
get: function () { return this; }
}); }
};
var ITERATOR$3 = _wks('iterator');
var SAFE_CLOSING = false;
try {
var riter = [7][ITERATOR$3]();
riter['return'] = function () { SAFE_CLOSING = true; };
// eslint-disable-next-line no-throw-literal
Array.from(riter, function () { throw 2; });
} catch (e) { /* empty */ }
var _iterDetect = function (exec, skipClosing) {
if (!skipClosing && !SAFE_CLOSING) { return false; }
var safe = false;
try {
var arr = [7];
var iter = arr[ITERATOR$3]();
iter.next = function () { return { done: safe = true }; };
arr[ITERATOR$3] = function () { return iter; };
exec(arr);
} catch (e) { /* empty */ }
return safe;
};
var task = _task.set;
var microtask = _microtask();
var PROMISE = 'Promise';
var TypeError$1 = _global$1.TypeError;
var process = _global$1.process;
var $Promise = _global$1[PROMISE];
var isNode = _classof(process) == 'process';
var empty = function () { /* empty */ };
var Internal;
var newGenericPromiseCapability;
var OwnPromiseCapability;
var Wrapper;
var newPromiseCapability = newGenericPromiseCapability = _newPromiseCapability.f;
var USE_NATIVE = !!function () {
try {
// correct subclassing with @@species support
var promise = $Promise.resolve(1);
var FakePromise = (promise.constructor = {})[_wks('species')] = function (exec) {
exec(empty, empty);
};
// unhandled rejections tracking support, NodeJS Promise without it fails @@species test
return (isNode || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise;
} catch (e) { /* empty */ }
}();
// helpers
var sameConstructor = _library ? function (a, b) {
// with library wrapper special case
return a === b || a === $Promise && b === Wrapper;
} : function (a, b) {
return a === b;
};
var isThenable = function (it) {
var then;
return _isObject(it) && typeof (then = it.then) == 'function' ? then : false;
};
var notify = function (promise, isReject) {
if (promise._n) { return; }
promise._n = true;
var chain = promise._c;
microtask(function () {
var value = promise._v;
var ok = promise._s == 1;
var i = 0;
var run = function (reaction) {
var handler = ok ? reaction.ok : reaction.fail;
var resolve = reaction.resolve;
var reject = reaction.reject;
var domain = reaction.domain;
var result, then;
try {
if (handler) {
if (!ok) {
if (promise._h == 2) { onHandleUnhandled(promise); }
promise._h = 1;
}
if (handler === true) { result = value; }
else {
if (domain) { domain.enter(); }
result = handler(value);
if (domain) { domain.exit(); }
}
if (result === reaction.promise) {
reject(TypeError$1('Promise-chain cycle'));
} else if (then = isThenable(result)) {
then.call(result, resolve, reject);
} else { resolve(result); }
} else { reject(value); }
} catch (e) {
reject(e);
}
};
while (chain.length > i) { run(chain[i++]); } // variable length - can't use forEach
promise._c = [];
promise._n = false;
if (isReject && !promise._h) { onUnhandled(promise); }
});
};
var onUnhandled = function (promise) {
task.call(_global$1, function () {
var value = promise._v;
var unhandled = isUnhandled(promise);
var result, handler, console;
if (unhandled) {
result = _perform(function () {
if (isNode) {
process.emit('unhandledRejection', value, promise);
} else if (handler = _global$1.onunhandledrejection) {
handler({ promise: promise, reason: value });
} else if ((console = _global$1.console) && console.error) {
console.error('Unhandled promise rejection', value);
}
});
// Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
promise._h = isNode || isUnhandled(promise) ? 2 : 1;
} promise._a = undefined;
if (unhandled && result.e) { throw result.v; }
});
};
var isUnhandled = function (promise) {
if (promise._h == 1) { return false; }
var chain = promise._a || promise._c;
var i = 0;
var reaction;
while (chain.length > i) {
reaction = chain[i++];
if (reaction.fail || !isUnhandled(reaction.promise)) { return false; }
} return true;
};
var onHandleUnhandled = function (promise) {
task.call(_global$1, function () {
var handler;
if (isNode) {
process.emit('rejectionHandled', promise);
} else if (handler = _global$1.onrejectionhandled) {
handler({ promise: promise, reason: promise._v });
}
});
};
var $reject = function (value) {
var promise = this;
if (promise._d) { return; }
promise._d = true;
promise = promise._w || promise; // unwrap
promise._v = value;
promise._s = 2;
if (!promise._a) { promise._a = promise._c.slice(); }
notify(promise, true);
};
var $resolve = function (value) {
var promise = this;
var then;
if (promise._d) { return; }
promise._d = true;
promise = promise._w || promise; // unwrap
try {
if (promise === value) { throw TypeError$1("Promise can't be resolved itself"); }
if (then = isThenable(value)) {
microtask(function () {
var wrapper = { _w: promise, _d: false }; // wrap
try {
then.call(value, _ctx($resolve, wrapper, 1), _ctx($reject, wrapper, 1));
} catch (e) {
$reject.call(wrapper, e);
}
});
} else {
promise._v = value;
promise._s = 1;
notify(promise, false);
}
} catch (e) {
$reject.call({ _w: promise, _d: false }, e); // wrap
}
};
// constructor polyfill
if (!USE_NATIVE) {
// 25.4.3.1 Promise(executor)
$Promise = function Promise(executor) {
_anInstance(this, $Promise, PROMISE, '_h');
_aFunction(executor);
Internal.call(this);
try {
executor(_ctx($resolve, this, 1), _ctx($reject, this, 1));
} catch (err) {
$reject.call(this, err);
}
};
// eslint-disable-next-line no-unused-vars
Internal = function Promise(executor) {
this._c = []; // <- awaiting reactions
this._a = undefined; // <- checked in isUnhandled reactions
this._s = 0; // <- state
this._d = false; // <- done
this._v = undefined; // <- value
this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled
this._n = false; // <- notify
};
Internal.prototype = _redefineAll($Promise.prototype, {
// 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)
then: function then(onFulfilled, onRejected) {
var reaction = newPromiseCapability(_speciesConstructor(this, $Promise));
reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
reaction.fail = typeof onRejected == 'function' && onRejected;
reaction.domain = isNode ? process.domain : undefined;
this._c.push(reaction);
if (this._a) { this._a.push(reaction); }
if (this._s) { notify(this, false); }
return reaction.promise;
},
// 25.4.5.1 Promise.prototype.catch(onRejected)
'catch': function (onRejected) {
return this.then(undefined, onRejected);
}
});
OwnPromiseCapability = function () {
var promise = new Internal();
this.promise = promise;
this.resolve = _ctx($resolve, promise, 1);
this.reject = _ctx($reject, promise, 1);
};
_newPromiseCapability.f = newPromiseCapability = function (C) {
return sameConstructor($Promise, C)
? new OwnPromiseCapability(C)
: newGenericPromiseCapability(C);
};
}
_export(_export.G + _export.W + _export.F * !USE_NATIVE, { Promise: $Promise });
_setToStringTag($Promise, PROMISE);
_setSpecies(PROMISE);
Wrapper = _core[PROMISE];
// statics
_export(_export.S + _export.F * !USE_NATIVE, PROMISE, {
// 25.4.4.5 Promise.reject(r)
reject: function reject(r) {
var capability = newPromiseCapability(this);
var $$reject = capability.reject;
$$reject(r);
return capability.promise;
}
});
_export(_export.S + _export.F * (_library || !USE_NATIVE), PROMISE, {
// 25.4.4.6 Promise.resolve(x)
resolve: function resolve(x) {
// instanceof instead of internal slot check because we should fix it without replacement native Promise core
if (x instanceof $Promise && sameConstructor(x.constructor, this)) { return x; }
return _promiseResolve(this, x);
}
});
_export(_export.S + _export.F * !(USE_NATIVE && _iterDetect(function (iter) {
$Promise.all(iter)['catch'](empty);
})), PROMISE, {
// 25.4.4.1 Promise.all(iterable)
all: function all(iterable) {
var C = this;
var capability = newPromiseCapability(C);
var resolve = capability.resolve;
var reject = capability.reject;
var result = _perform(function () {
var values = [];
var index = 0;
var remaining = 1;
_forOf(iterable, false, function (promise) {
var $index = index++;
var alreadyCalled = false;
values.push(undefined);
remaining++;
C.resolve(promise).then(function (value) {
if (alreadyCalled) { return; }
alreadyCalled = true;
values[$index] = value;
--remaining || resolve(values);
}, reject);
});
--remaining || resolve(values);
});
if (result.e) { reject(result.v); }
return capability.promise;
},
// 25.4.4.4 Promise.race(iterable)
race: function race(iterable) {
var C = this;
var capability = newPromiseCapability(C);
var reject = capability.reject;
var result = _perform(function () {
_forOf(iterable, false, function (promise) {
C.resolve(promise).then(capability.resolve, reject);
});
});
if (result.e) { reject(result.v); }
return capability.promise;
}
});
_export(_export.P + _export.R, 'Promise', { 'finally': function (onFinally) {
var C = _speciesConstructor(this, _core.Promise || _global$1.Promise);
var isFunction = typeof onFinally == 'function';
return this.then(
isFunction ? function (x) {
return _promiseResolve(C, onFinally()).then(function () { return x; });
} : onFinally,
isFunction ? function (e) {
return _promiseResolve(C, onFinally()).then(function () { throw e; });
} : onFinally
);
} });
// https://github.com/tc39/proposal-promise-try
_export(_export.S, 'Promise', { 'try': function (callbackfn) {
var promiseCapability = _newPromiseCapability.f(this);
var result = _perform(callbackfn);
(result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v);
return promiseCapability.promise;
} });
var promise$2 = _core.Promise;
var promise = createCommonjsModule(function (module) {
module.exports = { "default": promise$2, __esModule: true };
});
var asyncToGenerator = createCommonjsModule(function (module, exports) {
"use strict";
exports.__esModule = true;
var _promise2 = _interopRequireDefault(promise);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = function (fn) {
return function () {
var gen = fn.apply(this, arguments);
return new _promise2.default(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return _promise2.default.resolve(value).then(function (value) {
step("next", value);
}, function (err) {
step("throw", err);
});
}
}
return step("next");
});
};
};
});
var documentReady = createCommonjsModule(function (module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _regenerator2 = _interopRequireDefault(index$10);
var _promise2 = _interopRequireDefault(promise);
var _asyncToGenerator3 = _interopRequireDefault(asyncToGenerator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Await for this to run code after the DOM has been parsed and loaded (but not
* sub-resources like images, scripts, etc).
*
* @example
* ```js
* async function main() {
* await documentReady()
* console.log('Document ready!')
* }
* main()
* ```
*/
exports.default = function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee() {
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!(document.readyState === 'loading')) {
_context.next = 3;
break;
}
_context.next = 3;
return new _promise2.default(function (resolve) {
document.addEventListener('DOMContentLoaded', resolve);
});
case 3:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
function documentReady() {
return _ref.apply(this, arguments);
}
return documentReady;
}();
});
var documentReady$1 = unwrapExports(documentReady);
// TODO:
// - Finish lookAt from the camera tutorial.
let targetContextMap = new WeakMap;
function createWebGLContext(target, version) {
const canvas = createCanvas('100%', '100%');
const gl = getGl(canvas, version);
if (gl) {
if (targetContextMap.has(target)) { removeWebGLContext(target); }
target.appendChild(canvas);
targetContextMap.set(target, gl);
}
return gl
}
function removeWebGLContext(target) {
const gl = targetContextMap.get(target);
target.removeChild(gl.canvas);
}
function createCanvas(width, height) {
const canvas = document.createElement('canvas');
setCanvasCSSSize(canvas, width, height);
return canvas
}
function setCanvasCSSSize(canvas, width, height) {
canvas.style.width = width;
canvas.style.height = height;
}
function setGlResolution(gl, width, height) {
setCanvasRenderSize(gl.canvas, width, height);
gl.viewport(0, 0, width, height);
}
function setCanvasRenderSize(canvas, width, height) {
canvas.width = width;
canvas.height = height;
}
function getGl(canvasOrSelector, version) {
let canvas;
if (canvasOrSelector instanceof HTMLCanvasElement)
{ canvas = canvasOrSelector; }
if (!canvas)
{ canvas = document.querySelector(canvasOrSelector); }
if (!(canvas instanceof HTMLCanvasElement)) { return false }
if (version == 1 || version == undefined) { version = ''; }
else if (version == 2) { version = '2'; }
else { throw new Error('Invalid WebGL version.') }
return canvas.getContext('webgl'+version)
}
function createShader(gl, type, source) {
// Create a vertex shader object
const shader = gl.createShader(type);
// Attach vertex shader source code
gl.shaderSource(shader, source);
// Compile the vertex shader
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) { return shader }
const error = new Error("*** Error compiling shader '" + shader + "':" + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
throw error
}
function createProgram(gl, vertexShader, fragmentShader) {
// Create a shader program object to store
// the combined shader program
const program = gl.createProgram();
// Attach a vertex shader
gl.attachShader(program, vertexShader);
// Attach a fragment shader
gl.attachShader(program, fragmentShader);
// Link both programs
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program
}
console.log(' --- Error making program. GL Program Info Log:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
const v3 = {
cross(a, b) {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
]
},
subtract(a, b) {
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
},
add(a, b) {
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
},
normalize(v) {
const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
// make sure we don't divide by 0.
if (length > 0.00001) {
return [v[0] / length, v[1] / length, v[2] / length]
} else {
return [0, 0, 0]
}
},
};
var Geometry = function Geometry(...args) {
this._init(...args);
};
var prototypeAccessors$1 = { color: {} };
Geometry.prototype._init = function _init () {
this.verts = null; // Float32Array
this.normals = null; // Float32Array
this._colors = null; // Float32Array
this._color = null;
this._calcVerts();
this.color = [ 0.5, 0.5, 0.5 ];
};
// TODO handle CSS color strings with tinycolor2 from NPM
// @param {Array.number} value - array of four color values r, g, b, and a.
// TODO: don't use accept values for color alpha, use node's opacity.
prototypeAccessors$1.color.set = function (value) {
if (!value) { return }
this._color = value;
let color = null;
if (typeof value == 'string')
{ color = value.trim().split(' ').map(rgbPart => parseFloat(rgbPart)); }
else { color = value; }
// length of _colors array, considering it is four numbers per color,
// for each vertex.
// TODO: use a uniform instead of attributes that are all the same
// value.
const l = this.verts.length;
const _colorsLength = l + l/3;
const _colors = this._colors = new Float32Array(_colorsLength);
for (let i=0; i<_colorsLength; i+=4) { // 4 color parts per vertex
_colors[i+0] = color[0]; // r
_colors[i+1] = color[1]; // g
_colors[i+2] = color[2]; // b
_colors[i+3] = typeof color[3] == 'undefined' ? 1 : color[3]; // a
}
};
prototypeAccessors$1.color.get = function () {
return this._color
};
Object.defineProperties( Geometry.prototype, prototypeAccessors$1 );
var IsoscelesTriangle = (function (Geometry) {
function IsoscelesTriangle () {
Geometry.apply(this, arguments);
}
if ( Geometry ) IsoscelesTriangle.__proto__ = Geometry;
IsoscelesTriangle.prototype = Object.create( Geometry && Geometry.prototype );
IsoscelesTriangle.prototype.constructor = IsoscelesTriangle;
IsoscelesTriangle.prototype._init = function _init (width, height) {
this.width = width; // number
this.height = height; // number
Geometry.prototype._init.call(this);
};
IsoscelesTriangle.prototype._calcVerts = function _calcVerts () {
var ref = this;
var width = ref.width;
var height = ref.height;
const verts = this.verts = new Float32Array([
-width/2, 0, 0,
width/2, 0, 0,
0, height, 0,
]);
const normal = [0,0,1]; // pointing along Z
const normals = this.normals = new Float32Array(verts.length);
for (let i=0, l=verts.length; i<l; i+=3) { // 3 numbers per vertex
normals[i+0] = normal[0];
normals[i+1] = normal[1];
normals[i+2] = normal[2];
}
};
return IsoscelesTriangle;
}(Geometry));
var SymmetricTrapezoid = (function (Geometry) {
function SymmetricTrapezoid () {
Geometry.apply(this, arguments);
}
if ( Geometry ) SymmetricTrapezoid.__proto__ = Geometry;
SymmetricTrapezoid.prototype = Object.create( Geometry && Geometry.prototype );
SymmetricTrapezoid.prototype.constructor = SymmetricTrapezoid;
SymmetricTrapezoid.prototype._init = function _init (baseWidth, topWidth, height) {
this.baseWidth = baseWidth; // number
this.topWidth = topWidth; // number
this.height = height; // number
Geometry.prototype._init.call(this);
};
SymmetricTrapezoid.prototype._calcVerts = function _calcVerts () {
var ref = this;
var baseWidth = ref.baseWidth;
var topWidth = ref.topWidth;
var height = ref.height;
const verts = this.verts = new Float32Array([
-baseWidth/2, 0, 0,
baseWidth/2, 0, 0,
topWidth/2, height, 0,
topWidth/2, height, 0,
-topWidth/2, height, 0,
-baseWidth/2, 0, 0,
]);
const normal = [0,0,1]; // pointing along Z
const normals = this.normals = new Float32Array(verts.length);
for (let i=0, l=verts.length; i<l; i+=3) { // 3 numbers per vertex
normals[i+0] = normal[0];
normals[i+1] = normal[1];
normals[i+2] = normal[2];
}
};
return SymmetricTrapezoid;
}(Geometry));
var Quad = (function (Geometry) {
function Quad () {
Geometry.apply(this, arguments);
}
if ( Geometry ) Quad.__proto__ = Geometry;
Quad.prototype = Object.create( Geometry && Geometry.prototype );
Quad.prototype.constructor = Quad;
Quad.prototype._init = function _init (width, height) {
this.width = width; // number
this.height = height; // number
Geometry.prototype._init.call(this);
};
Quad.prototype._calcVerts = function _calcVerts () {
var ref = this;
var width = ref.width;
var height = ref.height;
const verts = this.verts = new Float32Array([
-width/2, -height/2, 0,
width/2, -height/2, 0,
width/2, height/2, 0,
width/2, height/2, 0,
-width/2, height/2, 0,
-width/2, -height/2, 0,
]);
const normal = [0,0,1]; // pointing along Z
const normals = this.normals = new Float32Array(verts.length);
for (let i=0, l=verts.length; i<l; i+=3) { // 3 numbers per vertex
normals[i+0] = normal[0];
normals[i+1] = normal[1];
normals[i+2] = normal[2];
}
};
return Quad;
}(Geometry));
var Cube = (function (Geometry) {
function Cube () {
Geometry.apply(this, arguments);
}
if ( Geometry ) Cube.__proto__ = Geometry;
Cube.prototype = Object.create( Geometry && Geometry.prototype );
Cube.prototype.constructor = Cube;
Cube.prototype._init = function _init (x, y, width) {
// the top front left corner
this.x = x; // number
this.y = y; // number
this.width = width; // number
Geometry.prototype._init.call(this);
};
Cube.prototype._calcVerts = function _calcVerts () {
var ref = this;
var x = ref.x;
var y = ref.y;
var width = ref.width;
const x2 = x + width;
const y2 = y + width;
const verts = this.verts = new Float32Array([
// front face
x, y, 0,
x2, y, 0,
x2, y2, 0,
x2, y2, 0,
x, y2, 0,
x, y, 0,
// left face
x, y, 0,
x, y, -width,
x, y2, -width,
x, y2, -width,
x, y2, 0,
x, y, 0,
// right face
x2, y, 0,
x2, y, -width,
x2, y2, -width,
x2, y2, -width,
x2, y2, 0,
x2, y, 0,
// back face
x, y, -width,
x2, y, -width,
x2, y2, -width,
x2, y2, -width,
x, y2, -width,
x, y, -width,
// top face
x, y, 0,
x, y, -width,
x2, y, -width,
x2, y, -width,
x2, y, 0,
x, y, 0,
// bottom face
x, y2, 0,
x, y2, -width,
x2, y2, -width,
x2, y2, -width,
x2, y2, 0,
x, y2, 0,
]);
const faceNormals = [
[0,0,1, ], // front face
[-1,0,0, ], // left face
[1,0,0,], // right face
[0,0,-1,], // back face
[0,-1,0, ], // top face
[0,1,0,], // bottom face
];
const normals = this.normals = new Float32Array(verts.length);
for (let side=0, i=0, l=verts.length; i<l; i+=6*3, side+=1) { // 6 vertices per side, 3 numbers per vertex normal
// first vertex
normals[i+0] = faceNormals[side][0];
normals[i+1] = faceNormals[side][1];
normals[i+2] = faceNormals[side][2];
// second vertex
normals[i+3] = faceNormals[side][0];
normals[i+4] = faceNormals[side][1];
normals[i+5] = faceNormals[side][2];
// third vertex
normals[i+6] = faceNormals[side][0];
normals[i+7] = faceNormals[side][1];
normals[i+8] = faceNormals[side][2];
// fourth vertex
normals[i+9] = faceNormals[side][0];
normals[i+10] = faceNormals[side][1];
normals[i+11] = faceNormals[side][2];
// fifth vertex
normals[i+12] = faceNormals[side][0];
normals[i+13] = faceNormals[side][1];
normals[i+14] = faceNormals[side][2];
// sixth vertex
normals[i+15] = faceNormals[side][0];
normals[i+16] = faceNormals[side][1];
normals[i+17] = faceNormals[side][2];
}
};
return Cube;
}(Geometry));
var FourSidedPyramid = (function (Geometry) {
function FourSidedPyramid () {
Geometry.apply(this, arguments);
}
if ( Geometry ) FourSidedPyramid.__proto__ = Geometry;
FourSidedPyramid.prototype = Object.create( Geometry && Geometry.prototype );
FourSidedPyramid.prototype.constructor = FourSidedPyramid;
FourSidedPyramid.prototype._init = function _init () {
Geometry.prototype._init.call(this);
};
FourSidedPyramid.prototype._calcVerts = function _calcVerts () {
this.verts = new Float32Array([
-100 ,0.087303 ,-100
,100 ,0.087303 ,-100
,100 ,0.087303 ,100
,-100 ,0.087303 ,100
,100 ,0.087303 ,100
,100 ,0.087303 ,-100
,0 ,200.087 ,0
,100 ,0.087303 ,-100
,-100 ,0.087303 ,-100
,0 ,200.087 ,0
,-100 ,0.087303 ,-100
,-100 ,0.087303 ,100
,0 ,200.087 ,0
,-100 ,0.087303 ,100
,100 ,0.087303 ,100
,0 ,200.087 ,0
]);
this.normals = new Float32Array([
0 ,-1 ,0
,0 ,-1 ,0
,0 ,-1 ,0
,0 ,-1 ,0
,0.894427 ,0.447214 ,0
,0.894427 ,0.447214 ,0
,0.894427 ,0.447214 ,0
,0 ,0.447214 ,-0.894427
,0 ,0.447214 ,-0.894427
,0 ,0.447214 ,-0.894427
,-0.894427 ,0.447214 ,0
,-0.894427 ,0.447214 ,0
,-0.894427 ,0.447214 ,0
,0 ,0.447214 ,0.894427
,0 ,0.447214 ,0.894427
,0 ,0.447214 ,0.894427
]);
};
return FourSidedPyramid;
}(Geometry));
const m3 = {
identity: Object.freeze([
1, 0, 0,
0, 1, 0,
0, 0, 1,
]),
translation(tx, ty) {
return [
1, 0, 0,
0, 1, 0,
tx, ty, 1,
]
},
rotation(angleInRadians) {
const c = Math.cos(angleInRadians);
const s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
]
},
scaling(sx, sy) {
return [
sx, 0, 0,
0, sy, 0,
0, 0, 1,
]
},
// Note: This matrix flips the Y axis so that 0 is at the top.
projection(width, height) {
// longer version, multiple matrices
let matrix = m3.identity;
matrix = m3.multiply(m3.scaling(1/width, 1/height), matrix); // get the portion of clip space
matrix = m3.multiply(m3.scaling(2, 2), matrix); // convert to clip space units
matrix = m3.multiply(m3.translation(-1, -1), matrix); // Move from the center to bottom left
matrix = m3.multiply(m3.scaling(1, -1), matrix); // move to the top left like DOM
return matrix
// shorter version, manual result of the longer version
//return [
//2 / width, 0, 0,
//0, -2 / height, 0,
//-1, 1, 1
//]
},
multiply(a, b) {
const a00 = a[0];
const a01 = a[1];
const a02 = a[2];
const a10 = a[3];
const a11 = a[4];
const a12 = a[5];
const a20 = a[6];
const a21 = a[7];
const a22 = a[8];
const b00 = b[0];
const b01 = b[1];
const b02 = b[2];
const b10 = b[3];
const b11 = b[4];
const b12 = b[5];
const b20 = b[6];
const b21 = b[7];
const b22 = b[8];
return [
b00 * a00 + b01 * a10 + b02 * a20,
b00 * a01 + b01 * a11 + b02 * a21,
b00 * a02 + b01 * a12 + b02 * a22,
b10 * a00 + b11 * a10 + b12 * a20,
b10 * a01 + b11 * a11 + b12 * a21,
b10 * a02 + b11 * a12 + b12 * a22,
b20 * a00 + b21 * a10 + b22 * a20,
b20 * a01 + b21 * a11 + b22 * a21,
b20 * a02 + b21 * a12 + b22 * a22,
]
},
};
const m4 = {
identity: Object.freeze([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]),
translation(tx, ty, tz) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1,
]
},
xRotation(degrees) {
const radians = degToRad(degrees);
const c = Math.cos(radians);
const s = Math.sin(radians);
return [
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, 0, 0, 1,
]
},
yRotation(degrees) {
const radians = degToRad(degrees);
const c = Math.cos(radians);
const s = Math.sin(radians);
return [
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
0, 0, 0, 1,
]
},
zRotation(degrees) {
const radians = degToRad(degrees);
const c = Math.cos(radians);
const s = Math.sin(radians);
return [
c,-s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]
},
scaling(sx, sy, sz) {
return [
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1,
]
},
inverse(m) {
const m00 = m[0 * 4 + 0];
const m01 = m[0 * 4 + 1];
const m02 = m[0 * 4 + 2];
const m03 = m[0 * 4 + 3];
const m10 = m[1 * 4 + 0];
const m11 = m[1 * 4 + 1];
const m12 = m[1 * 4 + 2];
const m13 = m[1 * 4 + 3];
const m20 = m[2 * 4 + 0];
const m21 = m[2 * 4 + 1];
const m22 = m[2 * 4 + 2];
const m23 = m[2 * 4 + 3];
const m30 = m[3 * 4 + 0];
const m31 = m[3 * 4 + 1];
const m32 = m[3 * 4 + 2];
const m33 = m[3 * 4 + 3];
const tmp_0 = m22 * m33;
const tmp_1 = m32 * m23;
const tmp_2 = m12 * m33;
const tmp_3 = m32 * m13;
const tmp_4 = m12 * m23;
const tmp_5 = m22 * m13;
const tmp_6 = m02 * m33;
const tmp_7 = m32 * m03;
const tmp_8 = m02 * m23;
const tmp_9 = m22 * m03;
const tmp_10 = m02 * m13;
const tmp_11 = m12 * m03;
const tmp_12 = m20 * m31;
const tmp_13 = m30 * m21;
const tmp_14 = m10 * m31;
const tmp_15 = m30 * m11;
const tmp_16 = m10 * m21;
const tmp_17 = m20 * m11;
const tmp_18 = m00 * m31;
const tmp_19 = m30 * m01;
const tmp_20 = m00 * m21;
const tmp_21 = m20 * m01;
const tmp_22 = m00 * m11;
const tmp_23 = m10 * m01;
const t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -
(tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
const t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -
(tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
const t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -
(tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
const t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -
(tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
const d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
return [
d * t0,
d * t1,
d * t2,
d * t3,
d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -
(tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -
(tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -
(tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -
(tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -
(tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -
(tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -
(tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -
(tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -
(tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -
(tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -
(tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -
(tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
]
},
transpose(m) {
return [
m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15],
]
},
// Note: This matrix flips the Y axis so that 0 is at the top.
projection(width, height, depth) {
// longer version, multiple matrices
//let matrix = m4.identity
//matrix = m4.multiply(m4.scaling(1/width, 1/height, 1/depth), matrix) // get the portion of clip space
//matrix = m4.multiply(m4.scaling(2, 2, 2), matrix) // convert to clip space units
//matrix = m4.multiply(m4.translation(-1, -1, 0), matrix) // Move from the center to bottom left
//matrix = m4.multiply(m4.scaling(1, -1, 1), matrix) // move to the top left like DOM
//return matrix
// shorter version, manual result of the longer version
return [
2 / width, 0, 0, 0,
0, -2 / height, 0, 0,
0, 0, 2 / depth, 0,
-1, 1, 0, 1,
]
},
// Note: This matrix flips the Y axis so that 0 is at the top.
orthographic(left, right, top, bottom, near, far) {
return [
2 / (right - left), 0, 0, 0,
0, 2 / (top - bottom), 0, 0,
0, 0, 2 / (near - far), 0,
(left + right) / (left - right),
(bottom + top) / (bottom - top),
(near + far) / (near - far),
1,
]
},
perspective(fieldOfViewInDegrees, aspect, near, far) {
const fieldOfViewInRadians = degToRad(fieldOfViewInDegrees);
const f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
const rangeInv = 1.0 / (near - far);
return [
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (near + far) * rangeInv, -1,
0, 0, near * far * rangeInv * 2, 0
]
},
lookAt(cameraPosition, target, up) {
const zAxis = v3.normalize(v3.subtract(cameraPosition, target));
const xAxis = v3.cross(up, zAxis);
const yAxis = v3.cross(zAxis, xAxis);
return [
xAxis[0], xAxis[1], xAxis[2], 0,
yAxis[0], yAxis[1], yAxis[2], 0,
zAxis[0], zAxis[1], zAxis[2], 0,
cameraPosition[0], cameraPosition[1], cameraPosition[2], 1,
];
},
multiply(a, b) {
const a00 = a[0 * 4 + 0];
const a01 = a[0 * 4 + 1];
const a02 = a[0 * 4 + 2];
const a03 = a[0 * 4 + 3];
const a10 = a[1 * 4 + 0];
const a11 = a[1 * 4 + 1];
const a12 = a[1 * 4 + 2];
const a13 = a[1 * 4 + 3];
const a20 = a[2 * 4 + 0];
const a21 = a[2 * 4 + 1];
const a22 = a[2 * 4 + 2];
const a23 = a[2 * 4 + 3];
const a30 = a[3 * 4 + 0];
const a31 = a[3 * 4 + 1];
const a32 = a[3 * 4 + 2];
const a33 = a[3 * 4 + 3];
const b00 = b[0 * 4 + 0];
const b01 = b[0 * 4 + 1];
const b02 = b[0 * 4 + 2];
const b03 = b[0 * 4 + 3];
const b10 = b[1 * 4 + 0];
const b11 = b[1 * 4 + 1];
const b12 = b[1 * 4 + 2];
const b13 = b[1 * 4 + 3];
const b20 = b[2 * 4 + 0];
const b21 = b[2 * 4 + 1];
const b22 = b[2 * 4 + 2];
const b23 = b[2 * 4 + 3];
const b30 = b[3 * 4 + 0];
const b31 = b[3 * 4 + 1];
const b32 = b[3 * 4 + 2];
const b33 = b[3 * 4 + 3];
return [
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
]
},
};
function degToRad(degrees) {
return degrees * Math.PI / 180
}
const vertShaderSource = `
attribute vec4 a_vertexPosition;
uniform mat4 u_worldViewProjectionMatrix;
// TODO: awaiting on transpose() method for DOMMatrix
//uniform mat4 u_worldInverseTransposeMatrix; // used for correct lighting normals
attribute vec4 a_color;
varying vec4 v_fragColor;
attribute vec3 a_normal;
varying vec3 v_vertNormal;
uniform mat4 u_worldMatrix;
uniform vec3 u_lightWorldPosition;
varying vec3 v_surfaceToLightVector;
uniform vec3 u_cameraWorldPosition;
varying vec3 v_surfaceToCameraVector;
attribute vec2 a_textureCoordinate;
varying vec2 v_textureCoordinate;
void main() {
vec3 surfaceWorldPosition = (u_worldMatrix * a_vertexPosition).xyz;
// compute the vector of the surface to the pointLight
// and pass it to the fragment shader
v_surfaceToLightVector = u_lightWorldPosition - surfaceWorldPosition;
// compute the vector of the surface to the camera
// and pass it to the fragment shader
v_surfaceToCameraVector = u_cameraWorldPosition - surfaceWorldPosition;
gl_Position = u_worldViewProjectionMatrix * a_vertexPosition;
v_fragColor = a_color;
//v_fragColor = gl_Position * 0.5 + 0.5;
// orient the normals and pass to the fragment shader
//v_vertNormal = mat3(u_worldInverseTransposeMatrix) * a_normal; // TODO waiting for transpose() method on DOMMatrix
//alternate: v_vertNormal = (u_worldInverseTransposeMatrix * vec4(a_normal, 0)).xyz;
v_vertNormal = mat3(u_worldMatrix) * a_normal;
v_textureCoordinate = a_textureCoordinate;
}
`;
const fragShaderSource = `
// TODO: detect highp support, see
// https://github.com/greggman/webgl-fundamentals/issues/80#issuecomment-306746556
//precision mediump float;
precision highp float;
varying vec4 v_fragColor;
varying vec3 v_vertNormal;
varying vec3 v_surfaceToLightVector;
//// TODO: use this for directional lighting (f.e. sunlight or moonlight).
//uniform vec3 reverseLightDirection;
varying vec3 v_surfaceToCameraVector;
uniform float u_shininess;
uniform vec3 u_lightColor;
uniform vec3 u_specularColor;
varying vec2 v_textureCoordinate;
uniform sampler2D u_texture;
uniform bool u_hasTexture;
void main(void) {
// because v_vertNormal is a varying it's interpolated
// so it will not be a unit vector. Normalizing it
// will make it a unit vector again.
vec3 normal = normalize(v_vertNormal);
vec3 surfaceToCameraDirection = normalize(v_surfaceToCameraVector);
vec3 surfaceToLightDirection = normalize(v_surfaceToLightVector);
// represents the unit vector oriented at half of the angle between
// surfaceToLightDirection and surfaceToCameraDirection.
vec3 halfVector = normalize(surfaceToLightDirection + surfaceToCameraDirection);
float pointLight = dot(normal, surfaceToLightDirection);
float pointLightIntensity = 1.0; // TODO make configurable
//float directionalLight = dot(normal, reverseLightDirection); // TODO make configurable
//float specular = dot(normal, halfVector);
float specular = 0.0;
if (pointLight > 0.0) {
specular = pow(dot(normal, halfVector), u_shininess);
}
// TODO make configurable
//vec3 ambientLight = vec3(0.361, 0.184, 0.737); // teal
vec3 ambientLight = vec3(1.0, 1.0, 1.0); // white
float ambientLightIntensity = 0.3;
// TODO: user can choose color or texture, default to a color if no texture, etc.
// TODO: blend texture on top of color, if texture has alpha.
gl_FragColor = v_fragColor;
if (u_hasTexture) {
gl_FragColor = texture2D(u_texture, v_textureCoordinate);
}
// Lets multiply just the color portion (not the alpha) of
// gl_FragColor by the pointLight + directionalLight
//gl_FragColor.rgb *= pointLight * u_lightColor; // point light only.
//gl_FragColor.rgb *= directionalLight; // directional light only.
//gl_FragColor.rgb *= ambientLight; // ambient light only.
gl_FragColor.rgb *=
//clamp(directionalLight, 0.0, 1.0) +
clamp(pointLight, 0.0, 1.0) * u_lightColor * pointLightIntensity +
ambientLight * ambientLightIntensity;
// Just add in the specular
gl_FragColor.rgb += specular * u_specularColor;
//gl_FragColor.a = 0.5;
}
`;
//import * as PIXI from 'pixi.js' // also sets the PIXI global.
//import SVG from 'pixi-svg' // uses the PIXI global, won't work if you don't import the main `pixi.js module`.
//import Two from 'two.js/build/two'
const updateResolution = state => {
const resolution = [
parseFloat(getComputedStyle(state.gl.canvas).width) * window.devicePixelRatio,
parseFloat(getComputedStyle(state.gl.canvas).height) * window.devicePixelRatio,
1000,
];
setGlResolution(state.gl, ...resolution);
state.projectionMatrix = m4.perspective(45, resolution[0] / resolution[1], 1, 2000);
};
var WebGlRenderer = function WebGlRenderer () {};
WebGlRenderer.prototype.initGl = function initGl (scene) {
const gl = createWebGLContext(scene);
const state = scene.webGlRendererState;
state.gl = gl;
if (!gl) { console.log('You need WebGL.'); }
const vertShader = createShader(gl, gl.VERTEX_SHADER, vertShaderSource);
const fragShader = createShader(gl, gl.FRAGMENT_SHADER, fragShaderSource);
const program = createProgram(gl, vertShader, fragShader);
gl.useProgram(program);
state.colorsBuffer = gl.createBuffer();
state.colorAttributeLocation = gl.getAttribLocation(program, 'a_color');
gl.enableVertexAttribArray(state.colorAttributeLocation);
state.vertexBuffer = gl.createBuffer();
state.vertexAttributeLocation = gl.getAttribLocation(program, "a_vertexPosition");
gl.enableVertexAttribArray(state.vertexAttributeLocation);
state.normalsBuffer = gl.createBuffer();
state.normalAttributeLocation = gl.getAttribLocation(program, 'a_normal');
gl.enableVertexAttribArray(state.normalAttributeLocation);
state.textureCoordinatesBuffer = gl.createBuffer();
state.textureCoordinateLocation = gl.getAttribLocation(program, 'a_textureCoordinate');
// cull_face doesn't work, because I've drawn my vertices in the wrong
// order. They should be clockwise to be front facing (I seem to have done
// them counter-clockwise). See "CULL_FACE" at
// https://webglfundamentals.org/webgl/lessons/webgl-3d-orthographic.html
//gl.enable(gl.CULL_FACE)
// enables depth sorting, so pixels aren't drawn in order of appearance, but order only if they are visible (on top of other pixels).
gl.enable(gl.DEPTH_TEST);
// enable alpha blending (transparency)
// XXX: For blending (transparency) to work, we have to disable depth testing.
// TODO: Maybe we have to selectively enable depth testing and disable
// blending, or vice versa, depending on the object we want to draw...
// ...Or perhaps we must draw things in a certain order, from back to front,
// so we can have depth testing AND blending at the same time.
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.enable(gl.BLEND);
//gl.disable(gl.DEPTH_TEST)
state.projectionMatrix = m4.identity;
updateResolution(state);
scene.on('parentsizechange', () => updateResolution(state));
state.worldViewProjectionMatrixLocation = gl.getUniformLocation(program, 'u_worldViewProjectionMatrix');
//const worldInverseTransposeMatrixLocation = gl.getUniformLocation(program, 'u_worldInverseTransposeMatrix')
state.worldMatrixLocation = gl.getUniformLocation(program, 'u_worldMatrix');
//const reverseLightDirectionLocation = gl.getUniformLocation(program, 'reverseLightDirection')
//gl.uniform3fv(reverseLightDirectionLocation, v3.normalize([0.5, 0.7, 1]))
state.lightWorldPositionLocation = gl.getUniformLocation(program, 'u_lightWorldPosition');
state.cameraWorldPositionLocation = gl.getUniformLocation(program, 'u_cameraWorldPosition');
const shininessLocation = gl.getUniformLocation(program, 'u_shininess');
const lightColorLocation = gl.getUniformLocation(program, 'u_lightColor');
const specularColorLocation = gl.getUniformLocation(program, 'u_specularColor');
state.textureLocation = gl.getUniformLocation(program, 'u_texture');
state.hasTextureLocation = gl.getUniformLocation(program, 'u_hasTexture');
let shininess = 200;
gl.uniform1f(shininessLocation, shininess);
const red = [1, 0.6, 0.6];
const white = [1, 1, 1];
let lightColor = white;
gl.uniform3fv(lightColorLocation, v3.normalize(lightColor));
let specularColor = white;
gl.uniform3fv(specularColorLocation, v3.normalize(specularColor));
state.lightAnimParam = 0;
state.lightWorldPosition = [20,30,50];
state.cameraAngle = 0;
state.cameraRadius = 200;
};
WebGlRenderer.prototype.drawScene = function drawScene (scene) {
const state = scene.webGlRendererState;
var gl = state.gl;
// TODO: light does not affect the back side of polygons?...
state.lightAnimParam += 0.05;
state.lightWorldPosition = [
300*Math.sin(state.lightAnimParam),
300*Math.sin(state.lightAnimParam*2),
Math.abs(300*Math.cos(state.lightAnimParam))
//300
];
gl.uniform3fv(state.lightWorldPositionLocation, state.lightWorldPosition);
let backgroundColor = scene.getAttribute('background');
if (typeof backgroundColor == 'string')
{ backgroundColor = backgroundColor.split(' ').map(rgbPart => parseFloat(rgbPart)); }
else
{ backgroundColor = [0, 0, 0, 0]; }
gl.clearColor(...backgroundColor);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // why do we need to do this?
//state.cameraAngle++
let cameraMatrix = m4.identity;
cameraMatrix = m4.multiply(cameraMatrix, m4.yRotation(state.cameraAngle));
cameraMatrix = m4.multiply(cameraMatrix, m4.translation(0, 0, state.cameraRadius * 1.5));
const viewMatrix = m4.inverse(cameraMatrix);
state.viewProjectionMatrix = m4.multiply(state.projectionMatrix, viewMatrix);
const cameraWorldPosition = [cameraMatrix[12], cameraMatrix[13], cameraMatrix[14]];
gl.uniform3fv(state.cameraWorldPositionLocation, cameraWorldPosition);
// TODO: we need to use the traversal that takes into consideration ShadowDOM.
const children = scene.imperativeCounterpart._children;
for (let i=0, l=children.length; i<l; i+=1) {
this.drawNodeAndRecurse(state, children[i]);
}
};
WebGlRenderer.prototype.drawNodeAndRecurse = function drawNodeAndRecurse (state, node) {
var gl = state.gl;
const meshAttr = node.element.getAttribute('mesh');
if (meshAttr) {
const size = node._calculatedSize;
const svgElement = Array.from(node.element.children)
.find(child => child instanceof SVGSVGElement);
const hasTexture = !!svgElement;
if (meshAttr == 'cube') {
if (!(node.__shape instanceof Cube))
{ node.__shape = new Cube(0, 0, size.x); }
// TODO else, like quad or symtrap
}
else if (meshAttr == 'quad') {
if (!(node.__shape instanceof Quad))
{ node.__shape = new Quad(size.x, size.y); }
else {
node.__shape.width = size.x;
node.__shape.height = size.y;
node.__shape._calcVerts();
}
if (hasTexture) {
// TODO we would create one per Geometry (and eventually multiple per
// geometry), but for now just one texture for all quads to get it working.
// TODO Make the texture only once, not each tick.
if (!node.__texture) {
// XXX this will eventually be set with a texture map feature
// TODO: for now, we should at least set default
// coordinates for each geometry, even if that's not
// ideal; it's more ideal than nothing.
node.__shape.textureCoordinates = new Float32Array([
0, 0,
1, 0,
1, 1,
1, 1,
0, 1,
0, 0,
]);
node.__texture = gl.createTexture();
}
///// SVG TEXTURE FROM TWO.JS {
if (!node.__two) {
node.__two = new Two({
type: Two.Types.webgl,
fullscreen: false,
autostart: false,
});
node.__two.interpret(svgElement);
}
node.__two.update();
const image = node.__two.renderer.domElement;
const isPowerOf2 = value => (value & (value - 1)) == 0;
// copy the pixi canvas image to the texture.
gl.bindTexture(gl.TEXTURE_2D, node.__texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// TODO: unbind from buffers and textures when done
// using them, to prevent modification from outside
// Mip maps can only be generated on images whose width and height are a power of 2.
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
gl.generateMipmap(gl.TEXTURE_2D);
// TODO make filters configurable?
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
// Using just NEAREST or LINEAR only can increase performance, for example.
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
}
else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// TODO make filters configurable?
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
///// }
///// SVG TEXTURE FROM PIXI-SVG {
//if (!node.__pixiRenderer) {
//node.__pixiRenderer = PIXI.autoDetectRenderer({
//width: node._calculatedSize.x * window.devicePixelRatio,
//height: node._calculatedSize.y * window.devicePixelRatio,
////width: 300 * window.devicePixelRatio,
////height: 300 * window.devicePixelRatio,
//resolution: window.devicePixelRatio,
//});
//node.__pixiStage = new PIXI.Container()
//window.stage = node.__pixiStage
//}
//node.__pixiStage.removeChild(node.__svgGraphic)
//node.__svgGraphic = new SVG(svgElement)
//node.__pixiStage.addChild(node.__svgGraphic)
//node.__pixiRenderer.render(node.__pixiStage);
//const image = node.__pixiRenderer.view
//const isPowerOf2 = value => (value & (value - 1)) == 0
//// copy the pixi canvas image to the texture.
//gl.bindTexture(gl.TEXTURE_2D, node.__texture)
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image)
//// TODO: unbind from buffers and textures when done
//// using them, to prevent modification from outside
//// Mip maps can only be generated on images whose width and height are a power of 2.
//if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
//gl.generateMipmap(gl.TEXTURE_2D)
//// TODO make filters configurable?
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
//// Using just NEAREST or LINEAR only can increase performance, for example.
////gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
////gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
//}
//else {
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
//// TODO make filters configurable?
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
//}
///// }
///// PRE-DEFINED TEXTURE FROM IMAGE {
//// set a temporary solid color texture for the meantime
//// while the following texture loads.
//gl.bindTexture(gl.TEXTURE_2D, node.__texture)
//// Fill the texture with a 1x1 blue pixel to start with.
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]))
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
//const image = new Image
//const isPowerOf2 = value => (value & (value - 1)) == 0
//image.addEventListener('load', () => {
//// Now that the image has loaded copy it to the texture.
//gl.bindTexture(gl.TEXTURE_2D, node.__texture)
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image)
//// TODO: unbind from buffers and textures when done
//// using them, to prevent modification from outside
//// Mip maps can only be generated on images whose width and height are a power of 2.
//if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
//gl.generateMipmap(gl.TEXTURE_2D)
//// TODO make filters configurable?
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
//// Using just NEAREST or LINEAR only can increase performance, for example.
////gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
////gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
//}
//else {
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
//// TODO make filters configurable?
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
//gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
//}
//})
//image.src = imageUrl // imageUrl should be a data URL
//// }
}
}
else if (meshAttr == 'isotriangle') {
if (!(node.__shape instanceof IsoscelesTriangle))
{ node.__shape = new IsoscelesTriangle(size.x, size.y); }
// TODO else, like quad or symtrap
}
else if (meshAttr == 'pyramid4') {
if (!(node.__shape instanceof FourSidedPyramid))
{ node.__shape = new FourSidedPyramid(size.x, size.y); }
// TODO else, like quad or symtrap
}
else if (meshAttr == 'symtrap') {
if (!(node.__shape instanceof SymmetricTrapezoid))
{ node.__shape = new SymmetricTrapezoid(size.x/2, size.x, size.y); }
else {
node.__shape.baseWidth = size.x/2;
node.__shape.topWidth = size.x;
node.__shape.height = size.y;
node.__shape._calcVerts();
}
}
//else node.__shape = null
else {
if (!(node.__shape instanceof Quad))
{ node.__shape = new Quad(size.x, size.y); }
else {
node.__shape.width = size.x;
node.__shape.height = size.y;
node.__shape._calcVerts();
}
// TODO this will eventually be set with a texture map feature
if (hasTexture) {
node.__shape.textureCoordinates = new Float32Array([
0, 0,
1, 0,
1, 1,
1, 1,
0, 1,
0, 0,
]);
}
}
if (node.__shape) {
// COLORS /////////////////////////////////
node.__shape.color = node.element.getAttribute('color');
gl.bindBuffer(gl.ARRAY_BUFFER, state.colorsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, node.__shape._colors, gl.STATIC_DRAW);
// Tell the attribute how to get data out of vertexBuffer (ARRAY_BUFFER)
const colorSize = 4; // components per iteration
const colorType = gl.FLOAT;
const normalizeColorData = false; // don't normalize the data
const colorStride = 0; // 0 = move forward colorSize * sizeof(colorType) each iteration to get the next vertex
const colorOffset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
state.colorAttributeLocation, colorSize, colorType, normalizeColorData, colorStride, colorOffset);
// VERTICES /////////////////////////////////
gl.bindBuffer(gl.ARRAY_BUFFER, state.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, node.__shape.verts, gl.STATIC_DRAW);
// Tell the attribute how to get data out of vertexBuffer (ARRAY_BUFFER)
const vertexSize = 3; // components per iteration
const type = gl.FLOAT;
const normalizeVertexData = false; // don't normalize the data
const stride = 0; // 0 = move forward vertexSize * sizeof(type) each iteration to get the next vertex
const offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
state.vertexAttributeLocation, vertexSize, type, normalizeVertexData, stride, offset);
// NORMALS /////////////////////////////////
gl.bindBuffer(gl.ARRAY_BUFFER, state.normalsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, node.__shape.normals, gl.STATIC_DRAW);
// Tell the attribute how to get data out of vertexBuffer (ARRAY_BUFFER)
const normalSize = 3; // components per iteration
const normalType = gl.FLOAT;
const normalizeNormalsData = false; // don't normalize the data
const normalStride = 0; // 0 = move forward normalSize * sizeof(normalType) each iteration to get the next vertex
const normalOffset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
state.normalAttributeLocation, normalSize, normalType, normalizeNormalsData, normalStride, normalOffset);
// TEXTURE COORDINATES /////////////////////////////////
if (hasTexture) {
gl.uniform1i(state.hasTextureLocation, +true);
gl.bindBuffer(gl.ARRAY_BUFFER, state.textureCoordinatesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, node.__shape.textureCoordinates, gl.STATIC_DRAW);
// Tell the attribute how to get data out of vertexBuffer (ARRAY_BUFFER)
const textureCoordinateSize = 2; // components per iteration
const textureCoordinateType = gl.FLOAT;
const normalizeTextureCoordinateData = false; // don't normalize the data
const textureCoordinateStride = 0; // 0 = move forward textureCoordinateSize * sizeof(textureCoordinateType) each iteration to get the next vertex
const textureCoordinateOffset = 0; // start at the beginning of the buffer
gl.enableVertexAttribArray(state.textureCoordinateLocation);
gl.vertexAttribPointer(
state.textureCoordinateLocation, textureCoordinateSize, textureCoordinateType, normalizeTextureCoordinateData, textureCoordinateStride, textureCoordinateOffset);
// Tell the shader to use texture unit 0 for u_texture
// TODO: Get index of the node's texture, but right now there's only one texture.
gl.uniform1i(state.textureLocation, 0);
}
else {
gl.uniform1i(state.hasTextureLocation, +false);
gl.disableVertexAttribArray(state.textureCoordinateLocation);
}
// TRANFORMS /////////////////////////////////
gl.uniformMatrix4fv(state.worldMatrixLocation, false, node._worldMatrix.toFloat32Array());
// for correct lighting normals
// TODO: waiting for transpose() method on DOMMatrix
//const worldInverseTransposeMatrix = m4.transpose(m4.inverse(node._worldMatrix))
//gl.uniformMatrix4fv(worldInverseTransposeMatrixLocation, false, worldInverseTransposeMatrix)
const worldViewProjectionMatrix = m4.multiply(state.viewProjectionMatrix, node._worldMatrix.toFloat32Array());
gl.uniformMatrix4fv(state.worldViewProjectionMatrixLocation, false, worldViewProjectionMatrix);
const count = node.__shape.verts.length / 3;
gl.drawArrays(gl.TRIANGLES, offset, count);
}
}
const children = node._children;
for (let i=0, l=children.length; i<l; i+=1) {
this.drawNodeAndRecurse(state, children[i]);
}
};
let instance = null;
function getWebGlRenderer() {
if (instance) { return instance }
else { return instance = new WebGlRenderer }
}
let documentIsReady = false;
let webGLRenderer = null;
var Motor = function Motor() {
this._inFrame = false; // true when inside a requested animation frame.
this._rAF = null; // the current animation frame, or null.
this._animationLoopStarted = false;
this._allRenderTasks = [];
this._taskIterationIndex = 0;
this._numberOfTasks = 0;
this._nodesToBeRendered = [];
this._modifiedScenes = [];
// A set of nodes that are the root nodes of subtrees where all nodes
// in each subtree need to have their world matrices updated.
this._worldMatrixRootNodes = [];
};
/**
* Starts an rAF loop and runs the render tasks in the _renderTasks stack.
* As long as there are tasks in the stack, the loop continues. When the
* stack becomes empty due to removal of tasks, the rAF stops and the app
* sits there doing nothing -- silence, crickets.
*/
Motor.prototype._startAnimationLoop = function _startAnimationLoop () {
if (this._animationLoopStarted) { return Promise.resolve() }
this._animationLoopStarted = true;
const logic = () => {
// DIRECT ANIMATION LOOP ///////////////////////////////////
// So now we can render after the scene is mounted.
const motorLoop = timestamp => {
this._inFrame = true;
this._runRenderTasks(timestamp);
this._renderNodes(timestamp);
// If any tasks are left to run, continue the animation loop.
if (this._allRenderTasks.length)
{ this._rAF = requestAnimationFrame(motorLoop); }
else {
this._rAF = null;
this._animationLoopStarted = false;
}
this._inFrame = false;
};
this._rAF = requestAnimationFrame(motorLoop);
};
if (!documentIsReady) {
return documentReady$1().then(() => {
documentIsReady = true;
logic();
})
}
logic();
return Promise.resolve()
};
//async _startAnimationLoop() {
//if (this._animationLoopStarted) return
//this._animationLoopStarted = true
//if (!documentIsReady) {
//await documentReady()
//documentIsReady = true
//}
//// DIRECT ANIMATION LOOP ///////////////////////////////////
//// So now we can render after the scene is mounted.
//const motorLoop = timestamp => {
//this._inFrame = true
//this._runRenderTasks(timestamp)
//this._renderNodes(timestamp)
//// If any tasks are left to run, continue the animation loop.
//if (this._allRenderTasks.length)
//this._rAF = requestAnimationFrame(motorLoop)
//else {
//this._rAF = null
//this._animationLoopStarted = false
//}
//this._inFrame = false
//}
//this._rAF = requestAnimationFrame(motorLoop)
//// ANIMATION LOOP USING WHILE AND AWAIT ///////////////////////////////////
////this._rAF = true
////let timestamp = null
////while (this._rAF) {
////timestamp = await animationFrame()
////this._inFrame = true
////this._runRenderTasks(timestamp)
////this._renderNodes(timestamp)
////// If any tasks are left to run, continue the animation loop.
////if (!this._allRenderTasks.length) {
////this._rAF = null
////this._animationLoopStarted = false
////}
////this._inFrame = false
////}
//}
/**
* When a render tasks is added a new rAF loop will be started if there
* isn't one currently.
*
* A render task is simply a function that will be called over and over
* again, in the Motor's animation loop. That's all, nothing special.
* However, if a Node setter is used inside of a render task, then the Node
* will tell Motor that it needs to be re-rendered, which will happen at
* the end of the current frame. If a Node setter is used outside of a
* render task (i.e. outside of the Motor's animation loop), then the Node
* tells Motor to re-render the Node on the next animation loop tick.
* Basically, regardless of where the Node's setters are used (inside or
* outside of the Motor's animation loop), rendering always happens inside
* the loop.
*
* @param {Function} fn The render task to add.
* @return {Function} A reference to the render task. Useful for saving to
* a variable so that it can later be passed to Motor.removeRenderTask().
*/
Motor.prototype.addRenderTask = function addRenderTask (fn) {
if (typeof fn != 'function')
{ throw new Error('Render task must be a function.') }
if (this._allRenderTasks.includes(fn)) { return }
this._allRenderTasks.push(fn);
this._numberOfTasks += 1;
// If the render loop isn't started, start it.
if (!this._animationLoopStarted)
{ this._startAnimationLoop(); }
return fn
};
Motor.prototype.removeRenderTask = function removeRenderTask (fn) {
const taskIndex = this._allRenderTasks.indexOf(fn);
if (taskIndex == -1) { return }
this._allRenderTasks.splice(taskIndex, 1);
this._numberOfTasks -= 1;
this._taskIterationIndex -= 1;
};
Motor.prototype._runRenderTasks = function _runRenderTasks (timestamp) {
for (this._taskIterationIndex = 0; this._taskIterationIndex < this._numberOfTasks; this._taskIterationIndex += 1) {
const task = this._allRenderTasks[this._taskIterationIndex];
if (task(timestamp) === false)
{ this.removeRenderTask(task); }
}
};
Motor.prototype._setNodeToBeRendered = function _setNodeToBeRendered (node) {
if (this._nodesToBeRendered.includes(node)) { return }
this._nodesToBeRendered.push(node);
if (!this._inFrame) { this._startAnimationLoop(); }
};
Motor.prototype._renderNodes = function _renderNodes (timestamp) {
if (!this._nodesToBeRendered.length) { return }
for (let i=0, l=this._nodesToBeRendered.length; i<l; i+=1) {
const node = this._nodesToBeRendered[i];
node._render(timestamp);
// If the node is root of a subtree containing updated nodes and
// has no ancestors that were modified, then add it to the
// _worldMatrixRootNodes set so we can update the world matrices of
// all the nodes in the root node's subtree.
if (
// a node could be a Scene, which is not Transformable
isInstanceof(node, Transformable) &&
// and if ancestor is not instanceof Transformable, f.e.
// `false` if there is no ancestor to be rendered, or Sizeable
// if the Scene is returned.
!isInstanceof(node._getAncestorToBeRendered(), Transformable) &&
// and the node isn't already added.
!this._worldMatrixRootNodes.includes(node)
) {
this._worldMatrixRootNodes.push(node);
}
// keep track of which scenes are modified so we can render webgl
// only for those scenes.
// TODO FIXME: at this point, a node should always have a scene,
// otherwise it should not ever be rendered here, but turns out
// some nodes are getting into this queue without a scene. We
// shouldn't need the conditional check for node._scene, and it
// will save CPU by not allowing the code to get here in that case.
if (node._scene && !this._modifiedScenes.includes(node._scene))
{ this._modifiedScenes.push(node._scene); }
}
// Update world matrices of the subtrees.
const worldMatrixRootNodes = this._worldMatrixRootNodes;
for (let i=0, l=worldMatrixRootNodes.length; i<l; i+=1) {
const subtreeRoot = worldMatrixRootNodes[i];
subtreeRoot._calculateWorldMatricesInSubtree();
}
worldMatrixRootNodes.length = 0;
// render webgl of modified scenes.
const modifiedScenes = this._modifiedScenes;
// TODO PERFORMANCE: store a list of webgl-enabled modified scenes, and
// iterate only through those so we don't iterate over non-webgl
// scenes.
for (let i=0, l=modifiedScenes.length; i<l; i+=1) {
const sceneElement = modifiedScenes[i].element;
// TODO we're temporarily storing stuff on the .element, but we
// don't want that, we will move it to WebGLRenderer.
if (
sceneElement.webglEnabled &&
( webGLRenderer || (webGLRenderer = getWebGlRenderer()) ) // only ever call getWebGlRenderer once
)
{ webGLRenderer.drawScene(sceneElement); }
}
modifiedScenes.length = 0;
const nodesToBeRendered = this._nodesToBeRendered;
for (let i=0, l=nodesToBeRendered.length; i<l; i+=1) {
nodesToBeRendered[i]._willBeRendered = false;
}
nodesToBeRendered.length = 0;
};
// export a singleton instance rather than the class directly.
var Motor$1 = new Motor;
// fallback to experimental CSS transform if browser doesn't have it (fix for Safari 9)
if (typeof document.createElement('div').style.transform == 'undefined') {
Object.defineProperty(CSSStyleDeclaration.prototype, 'transform', {
set(value) {
this.webkitTransform = value;
},
get() {
return this.webkitTransform
},
enumerable: true,
});
}
var XYZNonNegativeValues = (function (XYZValues$$1) {
function XYZNonNegativeValues(x, y, z) {
if ( x === void 0 ) x = 0;
if ( y === void 0 ) y = 0;
if ( z === void 0 ) z = 0;
XYZValues$$1.call(this, x, y, z);
}
if ( XYZValues$$1 ) XYZNonNegativeValues.__proto__ = XYZValues$$1;
XYZNonNegativeValues.prototype = Object.create( XYZValues$$1 && XYZValues$$1.prototype );
XYZNonNegativeValues.prototype.constructor = XYZNonNegativeValues;
var prototypeAccessors = { x: {},y: {},z: {} };
XYZNonNegativeValues.prototype._checkForNegative = function _checkForNegative (axisName, value) {
if(value < 0) {
throw new Error(axisName + " value was " + value + ". Size values cannot be negative.")
}
};
prototypeAccessors.x.set = function (value) {
this._checkForNegative("X", value);
XYZValues$$1.prototype.x = value;
};
prototypeAccessors.y.set = function (value) {
this._checkForNegative("Y", value);
XYZValues$$1.prototype.y = value;
};
prototypeAccessors.z.set = function (value) {
this._checkForNegative("Z", value);
XYZValues$$1.prototype.z = value;
};
Object.defineProperties( XYZNonNegativeValues.prototype, prototypeAccessors );
return XYZNonNegativeValues;
}(XYZValues));
const instanceofSymbol$3 = Symbol('instanceofSymbol');
const SizeableMixin = base => {
// Sizeable extends TreeNode because Sizeable knows about its _parent when
// calculating proportionalSize. Also Transformable knows about it's parent
// in order to calculate it's world matrix based on it's parent's.
var Sizeable = (function (superclass) {
function Sizeable(options) {
if ( options === void 0 ) options = {};
superclass.call(this, options);
this._propertyFunctions = null;
this._calculatedSize = { x:0, y:0, z:0 };
this._properties = {};
this._setDefaultProperties();
this._setPropertyObservers();
this.properties = options;
}
if ( superclass ) Sizeable.__proto__ = superclass;
Sizeable.prototype = Object.create( superclass && superclass.prototype );
Sizeable.prototype.constructor = Sizeable;
var prototypeAccessors = { sizeMode: {},absoluteSize: {},actualSize: {},proportionalSize: {},properties: {} };
Sizeable.prototype._setDefaultProperties = function _setDefaultProperties () {
Object.assign(this._properties, {
sizeMode: new XYZValues('absolute', 'absolute', 'absolute'),
absoluteSize: new XYZNonNegativeValues(0, 0, 0),
proportionalSize: new XYZNonNegativeValues(1, 1, 1),
});
};
Sizeable.prototype._setPropertyObservers = function _setPropertyObservers () {
this._properties.sizeMode.on('valuechanged',
() => this.triggerEvent('propertychange', 'sizeMode'));
this._properties.absoluteSize.on('valuechanged',
() => this.triggerEvent('propertychange', 'absoluteSize'));
this._properties.proportionalSize.on('valuechanged',
() => this.triggerEvent('propertychange', 'proportionalSize'));
};
Sizeable.prototype._calcSize = function _calcSize () {
const calculatedSize = this._calculatedSize;
const previousSize = Object.assign({}, calculatedSize);
const props = this._properties;
const parentSize = this._getParentSize();
if (props.sizeMode._x == 'absolute') {
calculatedSize.x = props.absoluteSize._x;
}
else { // proportional
calculatedSize.x = parentSize.x * props.proportionalSize._x;
}
if (props.sizeMode._y == 'absolute') {
calculatedSize.y = props.absoluteSize._y;
}
else { // proportional
calculatedSize.y = parentSize.y * props.proportionalSize._y;
}
if (props.sizeMode._z == 'absolute') {
calculatedSize.z = props.absoluteSize._z;
}
else { // proportional
calculatedSize.z = parentSize.z * props.proportionalSize._z;
}
if (
previousSize.x !== calculatedSize.x
|| previousSize.y !== calculatedSize.y
|| previousSize.z !== calculatedSize.z
) {
this.triggerEvent('sizechange', Object.assign({}, calculatedSize));
}
};
Sizeable.prototype._getParentSize = function _getParentSize () {
return this._parent ? this._parent._calculatedSize : {x:0,y:0,z:0}
};
Sizeable.prototype._setPropertyXYZ = function _setPropertyXYZ (Class, name, newValue) {
if (newValue instanceof Function) {
// remove previous task if any.
if (!this._propertyFunctions) { this._propertyFunctions = new Map; }
if (this._propertyFunctions.has(name))
{ Motor$1.removeRenderTask(this._propertyFunctions.get(name)); }
this._propertyFunctions.set(name,
Motor$1.addRenderTask(time => {
const result = newValue(
this._properties[name].x,
this._properties[name].y,
this._properties[name].z,
time
);
if (result === false) {
this._propertyFunctions.delete(name);
return false
}
this[name] = result;
})
);
}
else if (newValue instanceof Array) {
if (typeof newValue[0] != 'undefined') { this._properties[name].x = newValue[0]; }
if (typeof newValue[1] != 'undefined') { this._properties[name].y = newValue[1]; }
if (typeof newValue[2] != 'undefined') { this._properties[name].z = newValue[2]; }
}
else if (newValue instanceof Object) {
if (typeof newValue.x != 'undefined') { this._properties[name].x = newValue.x; }
if (typeof newValue.y != 'undefined') { this._properties[name].y = newValue.y; }
if (typeof newValue.z != 'undefined') { this._properties[name].z = newValue.z; }
}
else {
throw new TypeError(`Invalid value for ${Class.name}#${name}.`)
}
};
Sizeable.prototype._setPropertySingle = function _setPropertySingle (Class, name, newValue, type) {
if (!(typeof newValue == type || newValue instanceof Function))
{ throw new TypeError(`Invalid value for ${Class.name}#${name}.`) }
if (newValue instanceof Function) {
// remove previous task if any.
Motor$1.addRenderTask(time => {
const result = newValue(
this._properties[name],
time
);
if (result === false) { return false }
this[name] = result;
});
}
else {
this._properties[name] = newValue;
this.triggerEvent('propertychange', name);
}
};
Sizeable.prototype._render = function _render () {
// nothing yet, but needed because ImperativeBase calls
// `super._render()`, which will call either Transformable's
// _render or Sizeable's _render for Node and Scene classes,
// respectively.
};
/**
* Set the size mode for each axis. Possible size modes are "absolute" and "proportional".
*
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis sizeMode to apply.
* @param {number} [newValue.y] The y-axis sizeMode to apply.
* @param {number} [newValue.z] The z-axis sizeMode to apply.
*/
prototypeAccessors.sizeMode.set = function (newValue) {
this._setPropertyXYZ(Sizeable, 'sizeMode', newValue);
};
prototypeAccessors.sizeMode.get = function () {
return this._properties.sizeMode
};
/**
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis absoluteSize to apply.
* @param {number} [newValue.y] The y-axis absoluteSize to apply.
* @param {number} [newValue.z] The z-axis absoluteSize to apply.
*/
prototypeAccessors.absoluteSize.set = function (newValue) {
this._setPropertyXYZ(Sizeable, 'absoluteSize', newValue);
};
prototypeAccessors.absoluteSize.get = function () {
return this._properties.absoluteSize
};
/**
* Get the actual size of the Node. This can be useful when size is
* proportional, as the actual size of the Node depends on the size of
* it's parent.
*
* @readonly
*
* @return {Array.number} An Oject with x, y, and z properties, each
* property representing the computed size of the x, y, and z axes
* respectively.
*/
prototypeAccessors.actualSize.get = function () {
var ref = this._calculatedSize;
var x = ref.x;
var y = ref.y;
var z = ref.z;
return {x,y,z}
};
/**
* Set the size of a Node proportional to the size of it's parent Node. The
* values are a real number between 0 and 1 inclusive where 0 means 0% of
* the parent size and 1 means 100% of the parent size.
*
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis proportionalSize to apply.
* @param {number} [newValue.y] The y-axis proportionalSize to apply.
* @param {number} [newValue.z] The z-axis proportionalSize to apply.
*/
prototypeAccessors.proportionalSize.set = function (newValue) {
this._setPropertyXYZ(Sizeable, 'proportionalSize', newValue);
};
prototypeAccessors.proportionalSize.get = function () {
return this._properties.proportionalSize
};
/**
* Set all properties of a Sizeable in one method.
*
* @param {Object} properties Properties object - see example
*
* @example
* node.properties = {
* sizeMode: {x:'absolute', y:'proportional', z:'absolute'},
* absoluteSize: {x:300, y:100, z:200},
* proportionalSize: {x:1, z:0.5}
* }
*/
prototypeAccessors.properties.set = function (properties) {
if ( properties === void 0 ) properties = {};
if (properties.sizeMode)
{ this.sizeMode = properties.sizeMode; }
if (properties.absoluteSize)
{ this.absoluteSize = properties.absoluteSize; }
if (properties.proportionalSize)
{ this.proportionalSize = properties.proportionalSize; }
};
Object.defineProperties( Sizeable.prototype, prototypeAccessors );
return Sizeable;
}(TreeNode.mixin(Observable.mixin(base))));
// for use by MotorHTML, convenient since HTMLElement attributes are all
// converted to lowercase by default, so if we don't do this then we won't be
// able to map attributes to Node setters as easily.
makeLowercaseSetterAliases(Sizeable.prototype);
Object.defineProperty(Sizeable, Symbol.hasInstance, {
value: function(obj) {
if (this !== Sizeable) { return Object.getPrototypeOf(Sizeable)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while (currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol$3))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
Sizeable[instanceofSymbol$3] = true;
return Sizeable
};
const Sizeable = SizeableMixin((function () {
function anonymous () {}
return anonymous;
}()));
Sizeable.mixin = SizeableMixin;
const instanceofSymbol$1 = Symbol('instanceofSymbol');
const TransformableMixin = base => {
// Transformable extends TreeNode (indirectly through Sizeable) because it
// needs to be aware of its _parent when calculating align adjustments.
const ParentClass = Sizeable.mixin(base);
var Transformable = (function (ParentClass) {
function Transformable(options) {
if ( options === void 0 ) options = {};
ParentClass.call(this, options);
this._worldMatrix = null;
}
if ( ParentClass ) Transformable.__proto__ = ParentClass;
Transformable.prototype = Object.create( ParentClass && ParentClass.prototype );
Transformable.prototype.constructor = Transformable;
var prototypeAccessors = { position: {},rotation: {},scale: {},opacity: {},align: {},mountPoint: {},properties: {} };
Transformable.prototype._setDefaultProperties = function _setDefaultProperties () {
ParentClass.prototype._setDefaultProperties.call(this);
Object.assign(this._properties, {
position: new XYZValues(0, 0, 0),
rotation: new XYZValues(0, 0, 0),
scale: new XYZValues(1, 1, 1),
origin: new XYZValues(0.5, 0.5, 0.5),
align: new XYZValues(0, 0, 0),
mountPoint: new XYZValues(0, 0, 0),
opacity: 1,
transform: new window.DOMMatrix,
});
};
Transformable.prototype._setPropertyObservers = function _setPropertyObservers () {
ParentClass.prototype._setPropertyObservers.call(this);
this._properties.position.on('valuechanged',
() => this.triggerEvent('propertychange', 'position'));
this._properties.rotation.on('valuechanged',
() => this.triggerEvent('propertychange', 'rotation'));
this._properties.scale.on('valuechanged',
() => this.triggerEvent('propertychange', 'scale'));
this._properties.origin.on('valuechanged',
() => this.triggerEvent('propertychange', 'origin'));
this._properties.align.on('valuechanged',
() => this.triggerEvent('propertychange', 'align'));
this._properties.mountPoint.on('valuechanged',
() => this.triggerEvent('propertychange', 'mountPoint'));
};
/**
* Takes all the current component values (position, rotation, etc) and
* calculates a transformation DOMMatrix from them. See "W3C Geometry
* Interfaces" to learn about DOMMatrix.
*
* @method
* @private
* @memberOf Node
*/
Transformable.prototype._calculateMatrix = function _calculateMatrix () {
const matrix = new window.DOMMatrix;
const properties = this._properties;
const alignAdjustment = [0,0,0];
if (this._parent) { // The root Scene doesn't have a parent, for example.
const parentSize = this._parent._calculatedSize;
var align = properties.align;
alignAdjustment[0] = parentSize.x * align.x;
alignAdjustment[1] = parentSize.y * align.y;
alignAdjustment[2] = parentSize.z * align.z;
}
const mountPointAdjustment = [0,0,0];
const thisSize = this._calculatedSize;
var mountPoint = properties.mountPoint;
mountPointAdjustment[0] = thisSize.x * mountPoint.x;
mountPointAdjustment[1] = thisSize.y * mountPoint.y;
mountPointAdjustment[2] = thisSize.z * mountPoint.z;
const appliedPosition = [];
var position = properties.position;
appliedPosition[0] = position.x + alignAdjustment[0] - mountPointAdjustment[0];
appliedPosition[1] = position.y + alignAdjustment[1] - mountPointAdjustment[1];
appliedPosition[2] = position.z + alignAdjustment[2] - mountPointAdjustment[2];
matrix.translateSelf(appliedPosition[0], appliedPosition[1], appliedPosition[2]);
// origin calculation will go here:
// - move by negative origin before rotating.
// apply each axis rotation, in the x,y,z order.
var rotation = properties.rotation;
matrix.rotateAxisAngleSelf(1,0,0, rotation.x);
matrix.rotateAxisAngleSelf(0,1,0, rotation.y);
matrix.rotateAxisAngleSelf(0,0,1, rotation.z);
// origin calculation will go here:
// - move by positive origin after rotating.
return matrix
};
// TODO: fix _isIdentity in DOMMatrix, it is returning true even if false.
Transformable.prototype._calculateWorldMatricesInSubtree = function _calculateWorldMatricesInSubtree () {
this._calculateWorldMatrixFromParent();
const children = this._children;
for (let i=0, l=children.length; i<l; i+=1) {
children[i]._calculateWorldMatricesInSubtree();
}
};
Transformable.prototype._calculateWorldMatrixFromParent = function _calculateWorldMatrixFromParent () {
const parent = this._parent;
if (isInstanceof(parent, Transformable))
//this._worldMatrix = parent._worldMatrix.multiply(this._properties.transform)
{ this._worldMatrix = this._properties.transform.multiply(parent._worldMatrix); }
else // otherwise parent is the Scene, which is Sizeable, not Transformable
{ this._worldMatrix = this._properties.transform; }
};
Transformable.prototype._render = function _render () {
ParentClass.prototype._render.call(this);
// TODO: only run this when necessary (f.e. not if only opacity
// changed)
this._properties.transform = this._calculateMatrix();
};
/**
* Set the position of the Transformable.
*
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis position to apply.
* @param {number} [newValue.y] The y-axis position to apply.
* @param {number} [newValue.z] The z-axis position to apply.
*/
prototypeAccessors.position.set = function (newValue) {
this._setPropertyXYZ(Transformable, 'position', newValue);
};
prototypeAccessors.position.get = function () {
return this._properties.position
};
/**
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis rotation to apply.
* @param {number} [newValue.y] The y-axis rotation to apply.
* @param {number} [newValue.z] The z-axis rotation to apply.
*/
prototypeAccessors.rotation.set = function (newValue) {
this._setPropertyXYZ(Transformable, 'rotation', newValue);
};
prototypeAccessors.rotation.get = function () {
return this._properties.rotation
};
/**
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis scale to apply.
* @param {number} [newValue.y] The y-axis scale to apply.
* @param {number} [newValue.z] The z-axis scale to apply.
*/
prototypeAccessors.scale.set = function (newValue) {
this._setPropertyXYZ(Transformable, 'scale', newValue);
};
prototypeAccessors.scale.get = function () {
return this._properties.scale
};
/**
* Set this Node's opacity.
*
* @param {number} opacity A floating point number between 0 and 1
* (inclusive). 0 is fully transparent, 1 is fully opaque.
*/
prototypeAccessors.opacity.set = function (newValue) {
if (!isRealNumber(newValue)) { newValue = undefined; }
this._setPropertySingle(Transformable, 'opacity', newValue, 'number');
};
prototypeAccessors.opacity.get = function () {
return this._properties.opacity
};
/**
* Set the alignment of the Node. This determines at which point in this
* Node's parent that this Node is mounted.
*
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis align to apply.
* @param {number} [newValue.y] The y-axis align to apply.
* @param {number} [newValue.z] The z-axis align to apply.
*/
prototypeAccessors.align.set = function (newValue) {
this._setPropertyXYZ(Transformable, 'align', newValue);
};
prototypeAccessors.align.get = function () {
return this._properties.align
};
/**
* Set the mount point of the Node.
*
* @param {Object} newValue
* @param {number} [newValue.x] The x-axis mountPoint to apply.
* @param {number} [newValue.y] The y-axis mountPoint to apply.
* @param {number} [newValue.z] The z-axis mountPoint to apply.
*/
prototypeAccessors.mountPoint.set = function (newValue) {
this._setPropertyXYZ(Transformable, 'mountPoint', newValue);
};
prototypeAccessors.mountPoint.get = function () {
return this._properties.mountPoint
};
/**
* Set all properties of a Transformable in one method.
*
* @param {Object} properties Properties object - see example.
*
* @example
* node.properties = {
* position: {x:200, y:300, z:100},
* rotation: {z:35},
* scale: {y:2},
* opacity: .9,
* }
*/
prototypeAccessors.properties.set = function (properties) {
if ( properties === void 0 ) properties = {};
ParentClass.prototype.properties = properties;
if (properties.position)
{ this.position = properties.position; }
if (properties.rotation)
{ this.rotation = properties.rotation; }
if (properties.scale)
{ this.scale = properties.scale; }
if (properties.origin)
{ this.origin = properties.origin; }
if (properties.align)
{ this.align = properties.align; }
if (properties.mountPoint)
{ this.mountPoint = properties.mountPoint; }
if (properties.opacity)
{ this.opacity = properties.opacity; }
};
Object.defineProperties( Transformable.prototype, prototypeAccessors );
return Transformable;
}(ParentClass));
// for use by MotorHTML, convenient since HTMLElement attributes are all
// converted to lowercase by default, so if we don't do this then we won't be
// able to map attributes to Node setters as easily.
makeLowercaseSetterAliases(Transformable.prototype);
Object.defineProperty(Transformable, Symbol.hasInstance, {
value: function(obj) {
if (this !== Transformable) { return Object.getPrototypeOf(Transformable)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol$1))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
Transformable[instanceofSymbol$1] = true;
return Transformable
};
function isRealNumber(num) {
if (
typeof num != 'number'
|| Object.is(num, NaN)
|| Object.is(num, Infinity)
) { return false }
return true
}
const Transformable = TransformableMixin((function () {
function anonymous () {}
return anonymous;
}()));
Transformable.mixin = TransformableMixin;
var styles$1 = {
// all items of the scene graph are hidden until they are mounted in a
// scene (this changes to `display:block`).
display: 'none',
boxSizing: 'border-box',
position: 'absolute',
top: 0,
left: 0,
// Defaults to [0.5,0.5,0.5] (the Z axis doesn't apply for DOM elements,
// but will for 3D objects in WebGL.)
transformOrigin: '50% 50% 0', // default
transformStyle: 'preserve-3d',
};
var styles = Object.assign({}, styles$1,
{position: 'relative',
overflow: 'hidden',
width: '100%',
height: '100%',
// Constant perspective for now.
perspective: 1000});
/* global customElements */
// Very very stupid hack needed for Safari in order for us to be able to extend
// the HTMLElement class. See:
// https://github.com/google/traceur-compiler/issues/1709
if (typeof window.HTMLElement != 'function') {
const _HTMLElement = function HTMLElement(){};
_HTMLElement.prototype = window.HTMLElement.prototype;
window.HTMLElement = _HTMLElement;
}
const classCache = new Map;
function classExtendsHTMLElement(constructor) {
if (!constructor) { return false }
if (constructor === HTMLElement) { return true }
else { return classExtendsHTMLElement(constructor.prototype.__proto__ ? constructor.prototype.__proto__.constructor : null) }
}
/**
* Creates a WebComponent base class dynamically, depending on which
* HTMLElement class you want it to extend from. Extend from WebComponent when
* making a new Custom Element class.
*
* @example
* const WebComponent = WebComponentMixin(HTMLButtonElement)
* class AwesomeButton extends WebComponent { ... }
*
* @param {Function} elementClass The class that the generated WebComponent
* base class will extend from.
*/
function WebComponentMixin(elementClass) {
if (!elementClass) { elementClass = HTMLElement; }
if (!classExtendsHTMLElement(elementClass)) {
throw new TypeError(
'The argument to WebComponentMixin must be a constructor that extends from or is HTMLElement.'
)
}
// if a base class that extends the given `elementClass` has already been
// created, return it.
if (classCache.has(elementClass))
{ return classCache.get(elementClass) }
// otherwise, create it.
var WebComponent = (function (elementClass) {
function WebComponent() {
elementClass.call(this);
// Throw an error if no Custom Elements v1 API exists.
if (!('customElements' in window)) {
throw new Error(`
Your browser does not support the Custom Elements API. You'll
need to install a polyfill. See how at http://....
`)
}
this._connected = false;
this._initialized = false;
this._initialAttributeChange = false;
this._childObserver = null;
this._style = null;
}
if ( elementClass ) WebComponent.__proto__ = elementClass;
WebComponent.prototype = Object.create( elementClass && elementClass.prototype );
WebComponent.prototype.constructor = WebComponent;
var staticAccessors = { observedAttributes: {} };
// Subclasses can implement these.
WebComponent.prototype.childConnectedCallback = function childConnectedCallback (child) { };
WebComponent.prototype.childDisconnectedCallback = function childDisconnectedCallback (child) { };
WebComponent.prototype.connectedCallback = function connectedCallback () {
if (elementClass.prototype.connectedCallback) { elementClass.prototype.connectedCallback.call(this); }
this._connected = true;
if (!this._initialized) {
this.init();
this._initialized = true;
}
};
WebComponent.prototype._createStyles = function _createStyles () {
const rule = jss.createRule(this.getStyles());
rule.applyTo(this);
return rule
};
WebComponent.prototype.disconnectedCallback = function disconnectedCallback () {
if (elementClass.prototype.disconnectedCallback) { elementClass.prototype.disconnectedCallback.call(this); }
this._connected = false;
// Deferr to the next tick before cleaning up in case the
// element is actually being re-attached somewhere else within this
// same tick (detaching and attaching is synchronous, so by
// deferring to the next tick we'll be able to know if the element
// was re-attached or not in order to clean up or not). Note that
// appendChild can be used to move an element to another parent
// element, in which case connectedCallback and disconnectedCallback
// both get called, and in which case we don't necessarily want to
// clean up. If the element gets re-attached before the next tick
// (for example, gets moved), then we want to preserve the
// stuff that would be cleaned up by an extending class' deinit
// method by not running the following this.deinit() call.
Promise.resolve().then(() => { // deferr to the next tick.
// As mentioned in the previous comment, if the element was not
// re-attached in the last tick (for example, it was moved to
// another element), then clean up.
if (!this._connected && this._initialized) {
this.deinit();
}
});
};
//async disconnectedCallback() {
//if (super.disconnectedCallback) super.disconnectedCallback()
//this._connected = false
//// Deferr to the next tick before cleaning up in case the
//// element is actually being re-attached somewhere else within this
//// same tick (detaching and attaching is synchronous, so by
//// deferring to the next tick we'll be able to know if the element
//// was re-attached or not in order to clean up or not). Note that
//// appendChild can be used to move an element to another parent
//// element, in which case connectedCallback and disconnectedCallback
//// both get called, and in which case we don't necessarily want to
//// clean up. If the element gets re-attached before the next tick
//// (for example, gets moved), then we want to preserve the
//// stuff that would be cleaned up by an extending class' deinit
//// method by not running the following this.deinit() call.
//await Promise.resolve() // deferr to the next tick.
//// As mentioned in the previous comment, if the element was not
//// re-attached in the last tick (for example, it was moved to
//// another element), then clean up.
//if (!this._connected && this._initialized) {
//this.deinit()
//}
//}
/**
* This method can be overridden by extending classes, it should return
* JSS-compatible styling. See http://github.com/cssinjs/jss for
* documentation.
* @abstract
*/
WebComponent.prototype.getStyles = function getStyles () {
return {}
};
/**
* Init is called exactly once, the first time this element is
* connected into the DOM. When an element is disconnected then
* connected right away within the same synchronous tick, init() is not
* fired again. However, if an element is disconnected and the current
* tick completes before the element is connected again, then deinit()
* will be called (i.e. the element was not simply moved to a new
* location, it was actually removed), then the next time that the
* element is connected back into DOM init() will be called again.
*
* This is in contrast to connectedCallback and disconnectedCallback:
* connectedCallback is guaranteed to always fire even if the elemet
* was previously disconnected in the same synchronous tick.
*
* For example, ...
*
* Subclasses should extend this to add such logic.
*/
WebComponent.prototype.init = function init () {
if (!this._style) { this._style = this._createStyles(); }
// Handle any nodes that may have been connected before `this` node
// was created (f.e. child nodes that were connected before the
// custom elements were registered and which would therefore not be
// detected by the following MutationObserver).
if (!this._childObserver) {
const children = this.childNodes;
if (children.length) {
// Timeout needed in case the Custom Element classes are
// registered after the elements are already defined in the
// DOM but not yet upgraded. This means that the `node` arg
// might be a `<motor-node>` but if it isn't upgraded then
// its API won't be available to the logic inside the
// childConnectedCallback. The reason this happens is
// because parents are upgraded first and their
// connectedCallbacks fired before their children are
// upgraded.
//
// TODO FIXME PERFORMANCE: This causes a possibly "buggy" effect where
// elements in a tree will appear in intervals of 5
// milliseconds. We want elements to be rendered instantly,
// in the first frame that they are present in the scene
// graph.
// How can we fix this? Maybe we can switch to a Promise microtask.
setTimeout(() => {
for (let l=children.length, i=0; i<l; i+=1) {
this.childConnectedCallback(children[i]);
}
}, 5);
}
this._childObserver = observeChildren(this, this.childConnectedCallback, this.childDisconnectedCallback);
}
// fire this.attributeChangedCallback in case some attributes have
// existed before the custom element was upgraded.
if (!this._initialAttributeChange && this.hasAttributes()) {
// HTMLElement#attributes is a NamedNodeMap which is not an
// iterable, so we use Array.from. See:
// https://github.com/zloirock/core-js/issues/234
var ref = this;
var attributes = ref.attributes;
for (let l=attributes.length, i=0; i<l; i+=1)
{ this.attributeChangedCallback(attributes[i].name, null, attributes[i].value); }
}
};
staticAccessors.observedAttributes.get = function () {
console.warn(`WebComponent: Your custom element (${ this.name }) should specify observed attributes or attributeChangedCallback won't be called`);
};
WebComponent.prototype.attributeChangedCallback = function attributeChangedCallback (...args) {
//console.log(' --- attributeChangedCallback', typeof args[2])
if (elementClass.prototype.attributeChangedCallback) { elementClass.prototype.attributeChangedCallback.call(this, ...args); }
this._initialAttributeChange = true;
};
/**
* This is the reciprocal of init(). It will be called when an element
* has been disconnected but not re-connected within the same tick.
*
* The reason that init() and deinit() exist is so that if an element is
* moved from one place to another within the same synchronous tick,
* that deinit and init logic will not fire unnecessarily. If logic is
* needed in that case, then connectedCallback and disconnectedCallback
* can be used directly instead.
*/
WebComponent.prototype.deinit = function deinit () {
// Nothing much at the moment, but extending classes can extend
// this to add deintialization logic.
this._initialized = false;
};
Object.defineProperties( WebComponent, staticAccessors );
return WebComponent;
}(elementClass));
classCache.set(elementClass, WebComponent);
return WebComponent
}
initDeclarativeBase();
var HTMLNode = (function (DeclarativeBase$$1) {
function HTMLNode () {
DeclarativeBase$$1.apply(this, arguments);
}
if ( DeclarativeBase$$1 ) HTMLNode.__proto__ = DeclarativeBase$$1;
HTMLNode.prototype = Object.create( DeclarativeBase$$1 && DeclarativeBase$$1.prototype );
HTMLNode.prototype.constructor = HTMLNode;
var staticAccessors = { observedAttributes: {} };
HTMLNode.prototype.getStyles = function getStyles () {
return styles$1
};
// TODO: get these from somewhere dynamically, and do same for
// proxyGettersSetters and _updateNodeProperty
staticAccessors.observedAttributes.get = function () { return [
'sizeMode',
'absoluteSize',
'proportionalSize',
'align',
'mountPoint',
'rotation',
'position',
'scale',
'origin',
'skew',
'opacity',
].map(a => a.toLowerCase())};
HTMLNode.prototype.attributeChangedCallback = function attributeChangedCallback (...args) {
DeclarativeBase$$1.prototype.attributeChangedCallback.call(this, ...args);
this._updateNodeProperty(...args);
};
//async attributeChangedCallback(...args) {
//super.attributeChangedCallback(...args)
//this._updateNodeProperty(...args)
//}
HTMLNode.prototype._updateNodeProperty = function _updateNodeProperty (attribute, oldValue, newValue) {
// attributes on our HTML elements are the same name as those on
// the Node class (the setters).
if (newValue !== oldValue) {
if (attribute.match(/opacity/i))
{ this.imperativeCounterpart[attribute] = window.parseFloat(newValue); }
else if (attribute.match(/sizeMode/i))
{ this.imperativeCounterpart[attribute] = parseStringArray(newValue); }
else if (
attribute.match(/rotation/i)
|| attribute.match(/scale/i)
|| attribute.match(/position/i)
|| attribute.match(/absoluteSize/i)
|| attribute.match(/proportionalSize/i)
|| attribute.match(/align/i)
|| attribute.match(/mountPoint/i)
|| attribute.match(/origin/i)
|| attribute.match(/skew/i)
) {
this.imperativeCounterpart[attribute] = parseNumberArray(newValue);
}
else {
/* nothing, ignore other attributes */
}
}
};
Object.defineProperties( HTMLNode, staticAccessors );
return HTMLNode;
}(DeclarativeBase));
// This associates the Transformable getters/setters with the HTML-API classes,
// so that the same getters/setters can be called from HTML side of the API.
proxyGettersSetters(Transformable, HTMLNode);
proxyGettersSetters(Sizeable, HTMLNode);
function parseNumberArray(str) {
checkIsNumberArrayString(str);
const numbers = str.trim().split(/(?:\s*,\s*)|(?:\s+)/g);
const length = numbers.length;
if (length > 0) { numbers[0] = window.parseFloat(numbers[0]); }
if (length > 1) { numbers[1] = window.parseFloat(numbers[1]); }
if (length > 2) { numbers[2] = window.parseFloat(numbers[2]); }
return numbers
}
function parseStringArray(str) {
checkIsSizeArrayString(str);
const strings = str.trim().toLowerCase().split(/(?:\s*,\s*)|(?:\s+)/g);
const length = strings.length;
if (length > 0) { strings[0] = strings[0]; }
if (length > 1) { strings[1] = strings[1]; }
if (length > 2) { strings[2] = strings[2]; }
return strings
}
function checkIsNumberArrayString(str) {
if (!str.match(/^\s*(((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s*,){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))))|((\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+)))\s){0,2}(\s*(-|\+)?((\.\d+)|(\d+\.\d+)|(\d+)|(\d+(\.\d+)?e(-|\+)?(\d+))))))\s*$/g))
{ throw new Error(`Attribute must be a comma- or space-separated sequence of up to three numbers, for example "1 2.5 3". Yours was "${str}".`) }
}
function checkIsSizeArrayString(str) {
if (!str.match(/^\s*(((\s*([a-zA-Z]+)\s*,){0,2}(\s*([a-zA-Z]+)))|((\s*([a-zA-Z]+)\s*){1,3}))\s*$/g))
{ throw new Error(`Attribute must be a comma- or space-separated sequence of up to three strings, for example "absolute absolute". Yours was "${str}".`) }
}
/* global HTMLSlotElement */
var DeclarativeBase;
// We use this to Override HTMLElement.prototype.attachShadow in v1, and
// HTMLElement.prototype.createShadowRoot in v0, so that we can make the
// connection between parent and child on the iperative side when the HTML side
// is using shadow roots.
const observers = new WeakMap;
function hijack(original) {
return function(...args) {
// In v0, shadow roots can be replaced, but in v1 calling attachShadow
// on an element that already has a root throws. So, we can set this to
// true, and if the try-catch passes then we know we have a v0 root and
// that the root was just replaced.
const oldRoot = this.shadowRoot;
let root = null;
try {
root = original.call(this, ...args);
}
catch (e) { throw e }
if (this instanceof DeclarativeBase) {
this._hasShadowRoot = true;
if (oldRoot) {
onV0ShadowRootReplaced.call(this, oldRoot);
}
const observer = observeChildren(root, shadowRootChildAdded.bind(this), shadowRootChildRemoved.bind(this));
observers.set(root, observer);
var ref = this;
var children = ref.children;
for (let l=children.length, i=0; i<l; i+=1) {
if (!(children[i] instanceof DeclarativeBase)) { continue }
children[i]._isPossiblyDistributed = true;
}
}
return root
}
}
function shadowRootChildAdded(child) {
// NOTE Logic here is similar to childConnectedCallback
if (child instanceof DeclarativeBase) {
this.imperativeCounterpart.add(child.imperativeCounterpart);
}
else if (
hasShadowDomV0
&& child instanceof HTMLContentElement
) {
// observe <content> elements.
}
else if (
hasShadowDomV1
&& child instanceof HTMLSlotElement
) {
child.addEventListener('slotchange', this);
this._handleDistributedChildren(child);
}
}
function shadowRootChildRemoved(child) {
// NOTE Logic here is similar to childDisconnectedCallback
if (child instanceof DeclarativeBase) {
this.imperativeCounterpart.remove(child.imperativeCounterpart);
}
else if (
hasShadowDomV0
&& child instanceof HTMLContentElement
) {
// unobserve <content> element
}
else if (
hasShadowDomV1
&& child instanceof HTMLSlotElement
) {
child.removeEventListener('slotchange', this);
this._handleDistributedChildren(child);
this._slotElementsAssignedNodes.delete(child);
}
}
function onV0ShadowRootReplaced(oldRoot) {
observers.get(oldRoot).disconnect();
observers.delete(oldRoot);
var childNodes = oldRoot.childNodes;
for (let l=childNodes.length, i=0; i<l; i+=1) {
const child = childNodes[i];
if (!(child instanceof DeclarativeBase)) { continue }
// We should disconnect the imperative connection (f.e. so it is not
// rendered in WebGL)
this.imperativeCounterpart.remove(child.imperativeCounterpart, true);
}
}
if (HTMLElement.prototype.createShadowRoot instanceof Function)
{ HTMLElement.prototype.createShadowRoot = hijack(HTMLElement.prototype.createShadowRoot); }
if (HTMLElement.prototype.attachShadow instanceof Function)
{ HTMLElement.prototype.attachShadow = hijack(HTMLElement.prototype.attachShadow); }
initDeclarativeBase();
function initDeclarativeBase() {
if (DeclarativeBase) { return }
/**
* @implements {EventListener}
*/
DeclarativeBase = (function (superclass) {
function DeclarativeBase() {
superclass.call(this);
this.imperativeCounterpart = null; // to hold the imperative API Node instance.
// true if this node has a shadow root (even if it is "closed", see
// hijack function above). Once true always true because shadow
// roots cannot be removed.
this._hasShadowRoot = false;
// True when this node has a parent that has a shadow root. When
// using the HTML API, Imperative API can look at this to determine
// whether to render this node or not, in the case of WebGL.
this._isPossiblyDistributed = false;
// A map of the slot elements that are children of this node and
// their last-known assigned nodes. When a slotchange happens while
// this node is in a shadow root and has a slot child, we can
// detect what the difference is between the last known and the new
// assignments, and notate the new distribution of child nodes. See
// issue #40 for background on why we do this.
this._slotElementsAssignedNodes = new WeakMap;
// If this node is distributed into a shadow tree, this will
// reference the parent of the <slot> or <content> element.
// Basically, this node will render as a child of that parent node
// in the flat tree.
this._shadowParent = null;
// If this element has a child <slot> or <content> element while in
// a shadow root, then this will be a Set of the nodes distributed
// into the <slot> or <content>, and those nodes render relatively
// to this node in the flat tree. We instantiate this later, only
// when/if needed.
this._shadowChildren = null;
this._associateImperativeNode();
}
if ( superclass ) DeclarativeBase.__proto__ = superclass;
DeclarativeBase.prototype = Object.create( superclass && superclass.prototype );
DeclarativeBase.prototype.constructor = DeclarativeBase;
/**
* This method creates the association between this HTMLNode instance
* and the imperative Node instance.
*
* This method may get called by this.init, but can also be called by
* the Node class if Node is used imperatively. See Node#constructor.
*
* @private
*
* @param {Object} imperativeCounterpart The imperative counterpart to
* associate with this MotorHTML element. This parameter is only used in the
* imperative API constructors, and this happens when using the imperative
* form of infamous instead of the HTML interface to infamous. When the HTML
* interface is used, this gets called first without an
* imperativeCounterpart argument and the call to this in an imperative
* constructor will be a noop. Basically, either this gets called first by a
* MotorHTML element, or first by an imperative instance, depending on which
* API is used first.
*/
DeclarativeBase.prototype._associateImperativeNode = function _associateImperativeNode (imperativeCounterpart) {
// if the association is made already, noop
if (this.imperativeCounterpart) { return }
this.imperativeCounterpart = this;
};
DeclarativeBase.prototype.childConnectedCallback = function childConnectedCallback (child) {
// mirror the DOM connections in the imperative API's virtual scene graph.
if (child instanceof HTMLNode) {
if (this._hasShadowRoot) { child._isPossiblyDistributed = true; }
// If ImperativeBase#add was called first, child's
// _parent will already be set, so prevent recursion.
if (child.imperativeCounterpart._parent) { return }
this.imperativeCounterpart.add(child.imperativeCounterpart);
}
else if (
hasShadowDomV0
&& child instanceof HTMLContentElement
&&
//getShadowRootVersion(
getAncestorShadowRoot(this)
//) == 'v0'
) {
// observe <content> elements.
}
else if (
hasShadowDomV1
&& child instanceof HTMLSlotElement
&&
//getShadowRootVersion(
getAncestorShadowRoot(this)
//) == 'v1'
) {
child.addEventListener('slotchange', this);
this._handleDistributedChildren(child);
}
};
// This method is part of the EventListener interface.
DeclarativeBase.prototype.handleEvent = function handleEvent (event) {
if (event.type == 'slotchange') {
const slot = event.target;
this._handleDistributedChildren(slot);
}
};
DeclarativeBase.prototype._handleDistributedChildren = function _handleDistributedChildren (slot) {
const diff = this._getDistributedChildDifference(slot);
var added = diff.added;
for (let l=added.length, i=0; i<l; i+=1) {
const addedNode = added[i];
if (!(addedNode instanceof DeclarativeBase)) { continue }
// We do this because if the given slot is assigned to another
// slot, then this logic will run again for the next slot on
// that next slot's slotchange, so we remove the distributed
// node from the previous shadowParent and add it to the next
// one. If we don't do this, then the distributed node will
// exist in multiple shadowChildren lists when there is a
// chain of assigned slots. For more info, see
// https://github.com/w3c/webcomponents/issues/611
const shadowParent = addedNode._shadowParent;
if (shadowParent && shadowParent._shadowChildren) {
const shadowChildren = shadowParent._shadowChildren;
shadowChildren.splice(shadowChildren.indexOf(addedNode), 1);
if (!shadowChildren.length)
{ shadowParent._shadowChildren = null; }
}
addedNode._shadowParent = this;
if (!this._shadowChildren) { this._shadowChildren = []; }
this._shadowChildren.add(addedNode);
}
var removed = diff.removed;
for (let l=removed.length, i=0; i<l; i+=1) {
const removedNode = removed[i];
if (!(removedNode instanceof DeclarativeBase)) { continue }
removedNode._shadowParent = null;
this._shadowChildren.delete(removedNode);
if (!this._shadowChildren.size) { this._shadowChildren = null; }
}
};
DeclarativeBase.prototype._getDistributedChildDifference = function _getDistributedChildDifference (slot) {
let previousNodes;
if (this._slotElementsAssignedNodes.has(slot))
{ previousNodes = this._slotElementsAssignedNodes.get(slot); }
else
{ previousNodes = []; }
const newNodes = slot.assignedNodes({flatten: true});
// save the newNodes to be used as the previousNodes for next time.
this._slotElementsAssignedNodes.set(slot, newNodes);
const diff = {
removed: [],
};
for (let i=0, l=previousNodes.length; i<l; i+=1) {
const oldNode = previousNodes[i];
const newIndex = newNodes.indexOf(oldNode);
// if it exists in the previousNodes but not the newNodes, then
// the node was removed.
if (!(newIndex >= 0)) {
diff.removed.push(oldNode);
}
// otherwise the node wasn't added or removed.
else {
newNodes.splice(i, 1);
}
}
// Remaining nodes in newNodes must have been added.
diff.added = newNodes;
return diff
};
DeclarativeBase.prototype.childDisconnectedCallback = function childDisconnectedCallback (child) {
// mirror the connection in the imperative API's virtual scene graph.
if (child instanceof HTMLNode) {
child._isPossiblyDistributed = false;
// If ImperativeBase#remove was called first, child's
// _parent will already be null, so prevent recursion.
if (!child.imperativeCounterpart._parent) { return }
this.imperativeCounterpart.remove(child.imperativeCounterpart);
}
else if (
hasShadowDomV0
&& child instanceof HTMLContentElement
&&
//getShadowRootVersion(
getAncestorShadowRoot(this)
//) == 'v0'
) {
// unobserve <content> element
}
else if (
hasShadowDomV1
&& child instanceof HTMLSlotElement
&&
//getShadowRootVersion(
getAncestorShadowRoot(this)
//) == 'v1'
) {
child.removeEventListener('slotchange', this);
this._handleDistributedChildren(child);
this._slotElementsAssignedNodes.delete(child);
}
};
DeclarativeBase.prototype.setAttribute = function setAttribute (attr, value) {
//if (this.tagName.toLowerCase() == 'motor-scene')
//console.log('setting attribute', arguments[1])
superclass.prototype.setAttribute.call(this, attr, value);
};
return DeclarativeBase;
}(WebComponentMixin(window.HTMLElement)));
}
// Creates setters/getters on the TargetClass which proxy to the
// setters/getters on SourceClass.
function proxyGettersSetters(SourceClass, TargetClass) {
// Node methods not to proxy (private underscored methods are also detected and
// ignored).
const methodProxyBlacklist = [
'constructor',
'parent',
'children', // proxying this one would really break stuff (f.e. React)
'element',
'scene',
'add',
'addChildren',
'remove',
'removeChildren',
];
const props = Object.getOwnPropertyNames(SourceClass.prototype);
for (let l=props.length, i=0; i<l; i+=1) {
const prop = props[i];
if (
// skip the blacklisted properties
methodProxyBlacklist.indexOf(prop) >= 0
// skip the private underscored properties
|| prop.indexOf('_') == 0
// skip properties that are already defined.
|| TargetClass.prototype.hasOwnProperty(prop)
) { continue }
const targetDescriptor = {};
const sourceDescriptor = Object.getOwnPropertyDescriptor(SourceClass.prototype, prop);
// if the property has a setter
if (sourceDescriptor.set) {
Object.assign(targetDescriptor, {
set(value) {
this.imperativeCounterpart[prop] = value;
}
});
}
// if the property has a getter
if (sourceDescriptor.get) {
Object.assign(targetDescriptor, {
get() {
return this.imperativeCounterpart[prop]
}
});
}
Object.defineProperty(TargetClass.prototype, prop, targetDescriptor);
}
}
var sleep_1 = createCommonjsModule(function (module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise2 = _interopRequireDefault(promise);
exports.default = sleep;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Await for a certain amount of time.
*
* @example
* ```js
* async function main() {
* await sleep(10000)
* console.log('Logged after 10000 milliseconds!')
* }
* main()
* ```
*/
function sleep(duration) {
var resolve = null;
var promise$$2 = new _promise2.default(function (r) {
return resolve = r;
});
setTimeout(resolve, duration);
return promise$$2;
}
});
initDeclarativeBase();
var HTMLScene = (function (superclass) {
function HTMLScene() {
superclass.call(this);
this._sizePollTask = null;
this._parentSize = {x:0, y:0, z:0};
// If the scene is already in the DOM, make it be "mounted".
if (!this.imperativeCounterpart._mounted && this.parentNode)
{ this.imperativeCounterpart.mount(this.parentNode); }
}
if ( superclass ) HTMLScene.__proto__ = superclass;
HTMLScene.prototype = Object.create( superclass && superclass.prototype );
HTMLScene.prototype.constructor = HTMLScene;
HTMLScene.define = function define (name) {
customElements.define(name || 'i-scene', HTMLScene);
};
HTMLScene.prototype.__startSizePolling = function __startSizePolling () {
// NOTE Polling is currently required because there's no other way to do this
// reliably, not even with MutationObserver. ResizeObserver hasn't
// landed in browsers yet.
if (!this._sizePollTask)
{ this._sizePollTask = Motor$1.addRenderTask(this._checkSize.bind(this)); }
};
// NOTE, the Z dimension of a scene doesn't matter, it's a flat plane, so
// we haven't taken that into consideration here.
HTMLScene.prototype._checkSize = function _checkSize () {
// The scene has a parent by the time this is called (see
// src/core/Scene#mount where _startSizePolling is called)
const parent = this.parentNode;
const parentSize = this._parentSize;
const style = getComputedStyle(parent);
const width = parseFloat(style.width);
const height = parseFloat(style.height);
// if we have a size change, trigger parentsizechange
if (parentSize.x != width || parentSize.y != height) {
parentSize.x = width;
parentSize.y = height;
this.triggerEvent('parentsizechange', Object.assign({}, parentSize));
}
};
/** @override */
HTMLScene.prototype.getStyles = function getStyles () {
return styles
};
HTMLScene.prototype.deinit = function deinit () {
superclass.prototype.deinit.call(this);
this.imperativeCounterpart.unmount();
};
HTMLScene.prototype.__stopSizePolling = function __stopSizePolling () {
Motor$1.removeRenderTask(this._sizePollTask);
this._sizePollTask = null;
};
HTMLScene.prototype.connectedCallback = function connectedCallback () {
superclass.prototype.connectedCallback.call(this);
// When the HTMLScene gets addded to the DOM, make it be "mounted".
if (!this.imperativeCounterpart._mounted)
{ this.imperativeCounterpart.mount(this.parentNode); }
};
return HTMLScene;
}(Observable.mixin(DeclarativeBase)));
// This associates the Transformable getters/setters with the HTML-API classes,
// so that the same getters/setters can be called from HTML side of the API.
proxyGettersSetters(Sizeable, HTMLScene);
// although Transformable is not used in this file, importing it first prevents
// a cyclical dependeny problem when an app entrypoint imports ./Scene.js
// before ./Node.js (Sizeable imports Motor which imports Transformable which
// imports Sizeable). See:
// https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem
// TODO: write a test that imports public interfaces in every possible
// permutation to detect circular dependency errors.
//
// Transformable is not used in this file, but importing here solves the
// circular dependency problem.
// Sizeable is used in this file.
initImperativeBase();
const instanceofSymbol$5 = Symbol('instanceofSymbol');
let Scene = null;
const SceneMixin = base => {
// Scene is Sizeable, which is currently a subset of Transformable.
var _Scene = (function (superclass) {
function _Scene(options) {
if ( options === void 0 ) options = {};
superclass.call(this, options);
// NOTE: z size is always 0, since native DOM elements are always flat.
this._elementParentSize = {x:0, y:0, z:0};
this._onElementParentSizeChange = (newSize) => {
this._elementParentSize = newSize;
this._calcSize();
this._needsToBeRendered();
};
this._calcSize();
this._needsToBeRendered();
// For now, use the same program (with shaders) for all objects.
// Basically it has position, frag colors, point light, directional
// light, and ambient light.
// TODO: maybe call this in `init()`, and destroy webgl stuff in
// `deinit()`.
// TODO: The user might enable this by setting the attribute later, so
// we can't simply rely on having it in constructor, we need a
// getter/setter like node properties.
this.initWebGl();
}
if ( superclass ) _Scene.__proto__ = superclass;
_Scene.prototype = Object.create( superclass && superclass.prototype );
_Scene.prototype.constructor = _Scene;
var prototypeAccessors = { sizeMode: {} };
// TODO: we need to deinit webgl too.
_Scene.define = function define (name) {
customElements.define(name || 'i-scene', Scene);
};
_Scene.prototype.initWebGl = function initWebGl () {
// TODO: this needs to be cancelable too, search other codes for
// "mountcancel" to see.
this.mountPromise.then(() => {
this.webglEnabled = !!this.getAttribute('webglenabled');
if (!this.webglEnabled) { return }
this.webGlRendererState = {};
getWebGlRenderer().initGl(this);
});
};
//async initWebGl() {
//// TODO: this needs to be cancelable too, search other codes for
//// "mountcancel" to see.
//await this.mountPromise
//this.webglEnabled = !!this.getAttribute('webglenabled')
//if (!this.webglEnabled) return
//this.webGlRendererState = {}
//getWebGlRenderer().initGl(this)
//}
_Scene.prototype._setDefaultProperties = function _setDefaultProperties () {
superclass.prototype._setDefaultProperties.call(this);
Object.assign(this._properties, {
sizeMode: new XYZValues('proportional', 'proportional', 'absolute'),
});
};
_Scene.prototype._startOrStopSizePolling = function _startOrStopSizePolling () {
if (
this._mounted &&
(this._properties.sizeMode.x == 'proportional'
|| this._properties.sizeMode.y == 'proportional'
|| this._properties.sizeMode.z == 'proportional')
) {
this._startSizePolling();
}
else {
this._stopSizePolling();
}
};
// observe size changes on the scene element.
_Scene.prototype._startSizePolling = function _startSizePolling () {
if (!this._elementManager) { return }
this._elementManager.element.__startSizePolling();
this._elementManager.element.on('parentsizechange', this._onElementParentSizeChange);
};
// Don't observe size changes on the scene element.
_Scene.prototype._stopSizePolling = function _stopSizePolling () {
if (!this._elementManager) { return }
this._elementManager.element.off('parentsizechange', this._onElementParentSizeChange);
this._elementManager.element.__stopSizePolling();
};
/** @override */
_Scene.prototype._getParentSize = function _getParentSize () {
return this._mounted ? this._elementParentSize : {x:0,y:0,z:0}
};
/**
* Mount the scene into the given target.
* Resolves the Scene's mountPromise, which can be use to do something once
* the scene is mounted.
*
* @param {string|HTMLElement} [mountPoint=document.body] If a string selector is provided,
* the mount point will be selected from the DOM. If an HTMLElement is
* provided, that will be the mount point. If no mount point is provided,
* the scene will be mounted into document.body.
*/
_Scene.prototype.mount = function mount (mountPoint) {
const mountLogic = () => {
// if no mountPoint was provided, just mount onto the <body> element.
if (mountPoint === undefined) { mountPoint = document.body; }
// if the user supplied a selector, mount there.
else if (typeof mountPoint === 'string')
{ mountPoint = document.querySelector(mountPoint); }
// if we have an actual mount point (the user may have supplied one)
if (!(mountPoint instanceof window.HTMLElement))
{ throw new Error('Invalid mount point specified in Scene.mount() call. Pass a selector, an actual HTMLElement, or don\'t pass anything to mount to <body>.') }
if (this._mounted) { this.unmount(); }
if (mountPoint !== this._elementManager.element.parentNode)
{ mountPoint.appendChild(this._elementManager.element); }
this._mounted = true;
if (this._mountPromise) { this._resolveMountPromise(); }
this._elementManager.shouldRender();
this._startOrStopSizePolling();
};
// Wait for the document to be ready before mounting, otherwise the
// target mount point might not exist yet when this function is called.
if (document.readyState == 'loading') { return documentReady$1().then(mountLogic) }
else {
mountLogic();
return Promise.resolve()
}
};
//async mount(mountPoint) {
//// Wait for the document to be ready before mounting, otherwise the
//// target mount point might not exist yet when this function is called.
//if (document.readyState == 'loading') await documentReady()
//// if no mountPoint was provided, just mount onto the <body> element.
//if (mountPoint === undefined) mountPoint = document.body
//// if the user supplied a selector, mount there.
//else if (typeof mountPoint === 'string')
//mountPoint = document.querySelector(mountPoint)
//// if we have an actual mount point (the user may have supplied one)
//if (!(mountPoint instanceof window.HTMLElement))
//throw new Error('Invalid mount point specified in Scene.mount() call. Pass a selector, an actual HTMLElement, or don\'t pass anything to mount to <body>.')
//if (this._mounted) this.unmount()
//if (mountPoint !== this._elementManager.element.parentNode)
//mountPoint.appendChild(this._elementManager.element)
//this._mounted = true
//if (this._mountPromise) this._resolveMountPromise()
//this._elementManager.shouldRender()
//this._startOrStopSizePolling()
//}
/**
* Unmount the scene from it's mount point. Resets the Scene's
* mountPromise.
*/
_Scene.prototype.unmount = function unmount () {
if (!this._mounted) { return }
this._elementManager.shouldNotRender();
this._stopSizePolling();
if (this._elementManager.element.parentNode)
{ this._elementManager.element.parentNode.removeChild(this._elementManager.element); }
if (this._mountPromise) { this._rejectMountPromise('mountcancel'); }
this._resetMountPromise();
};
prototypeAccessors.sizeMode.set = function (value) {
superclass.prototype.sizeMode = value;
this._startOrStopSizePolling();
};
Object.defineProperties( _Scene.prototype, prototypeAccessors );
return _Scene;
}(ImperativeBase.mixin(Sizeable.mixin(base))));
Object.defineProperty(_Scene, Symbol.hasInstance, {
value: function(obj) {
if (this !== _Scene) { return Object.getPrototypeOf(_Scene)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol$5))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
_Scene[instanceofSymbol$5] = true;
return _Scene
};
Scene = SceneMixin((function () {
function anonymous () {}
return anonymous;
}()));
Scene.mixin = SceneMixin;
// for now, hard-mixin the HTMLScene class. We'll do this automatically later.
Scene = Scene.mixin(HTMLScene);
// We explicitly use `var` instead of `let` here because it is hoisted for the
// Node and Scene modules. This, along with the following initImperativeBase
// function, allows the circular dependency between this module and the Node and
// Scene modules to work. For details on why, see
// https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem.
var ImperativeBase;
// Here we wrap the definition of the ImperativeBase class with this function in
// order to solve the circular depdendency problem caused by the
// Node<->ImperativeBase and Scene<->ImperativeBase circles. The Node and Scene
// modules call initImperativeBase to ensure that the ImperativeBase declaration
// happens first, and then those modules can use the live binding in their
// declarations.
initImperativeBase();
function initImperativeBase() {
if (ImperativeBase) { return }
const instanceofSymbol = Symbol('instanceofSymbol');
/**
* The ImperativeBase class is the base class for the Imperative version of the
* API, for people who chose to take the all-JavaScript approach and who will
* not use the HTML-based API (infamous/motor-html).
*
* In the future when there is an option to disable the HTML-DOM rendering (and
* render only WebGL, for example) then the imperative API will be the only API
* available since the HTML API will be turned off as a result of disabling
* HTML rendering. Disabling both WebGL and HTML won't make sense, as we'll need
* at least one of those to render with.
*/
const ImperativeBaseMixin = base => {
const ParentClass = base;
var ImperativeBase = (function (ParentClass) {
function ImperativeBase(options) {
if ( options === void 0 ) options = {};
ParentClass.call(this, options);
this._willBeRendered = false;
// Here we create the DOM HTMLElement associated with this
// Imperative-API Node.
this._elementManager = new ElementManager(this);
this._elementManager.element._associateImperativeNode(this);
// For Nodes, true when this Node is added to a parent AND it
// has an anancestor Scene that is mounted into DOM. For
// Scenes, true when mounted into DOM.
this._mounted = false;
// For Nodes, a promise that resolves when this Node is
// attached to a tree that has a root Scene TreeNode *and* when
// that root Scene has been mounted into the DOM. For Scenes,
// resolves when mounted into DOM.
this._mountPromise = null;
this._resolveMountPromise = null;
this._rejectMountPromise = null;
this._awaitingMountPromiseToRender = false;
this._waitingForMountConditions = false;
// See Transformable/Sizeable propertychange event.
this.on('propertychange', prop => {
if (
prop == 'sizeMode' ||
prop == 'absoluteSize' ||
prop == 'proportionalSize'
) {
this._calcSize();
}
this._needsToBeRendered();
});
}
if ( ParentClass ) ImperativeBase.__proto__ = ParentClass;
ImperativeBase.prototype = Object.create( ParentClass && ParentClass.prototype );
ImperativeBase.prototype.constructor = ImperativeBase;
var prototypeAccessors = { mountPromise: {},element: {},properties: {} };
/**
* Subclasses are required to override this. It should return the HTML-API
* counterpart for this Imperative-API instance. See Node or Scene classes
* for example.
*
* @private
*/
ImperativeBase.prototype._makeElement = function _makeElement () {
throw new Error('Subclasses need to override ImperativeBase#_makeElement.')
};
/**
* @readonly
*/
prototypeAccessors.mountPromise.get = function () {
if (!this._mountPromise) {
this._mountPromise = new Promise((resolve, reject) => {
this._resolveMountPromise = resolve;
this._rejectMountPromise = reject;
});
}
if (!this._mounted)
{ this._waitForMountThenResolveMountPromise(); }
else if (this._mounted)
{ this._resolveMountPromise(); }
return this._mountPromise
};
ImperativeBase.prototype._waitForMountThenResolveMountPromise = function _waitForMountThenResolveMountPromise () {
// extended in Node or Scene to await for anything that mount
// depends on.
};
/**
* @readonly
*/
prototypeAccessors.element.get = function () {
return this._elementManager.element
};
/**
* @override
*/
ImperativeBase.prototype.add = function add (childNode) {
if (!isInstanceof(childNode, ImperativeBase)) { return }
// We cannot add Scenes to Nodes, for now.
if (childNode instanceof Scene) {
throw new Error(`
A Scene cannot be added to another Node or Scene (at
least for now). To place a Scene in a Node, just mount
a new Scene onto a MotorHTMLNode with Scene.mount().
`)
}
ParentClass.prototype.add.call(this, childNode);
// Pass this parent node's Scene reference (if any, checking this cache
// first) to the new child and the child's children.
if (childNode._scene || childNode.scene) {
if (childNode._resolveScenePromise)
{ childNode._resolveScenePromise(childNode._scene); }
childNode._giveSceneRefToChildren();
}
// Calculate sizing because proportional size might depend on
// the new parent.
childNode._calcSize();
childNode._needsToBeRendered();
// child should watch the parent for size changes.
this.on('sizechange', childNode._onParentSizeChange);
this._elementManager.connectChildElement(childNode);
return this
};
ImperativeBase.prototype.remove = function remove (childNode, /*private use*/leaveInDom) {
if (!(childNode instanceof Node$1)) { return }
ParentClass.prototype.remove.call(this, childNode);
this.off('sizechange', childNode._onParentSizeChange);
childNode._resetSceneRef();
if (childNode._mountPromise) { childNode._rejectMountPromise('mountcancel'); }
if (childNode._mounted) { childNode._elementManager.shouldNotRender(); }
childNode._resetMountPromise();
if (!leaveInDom)
{ this._elementManager.disconnectChildElement(childNode); }
};
ImperativeBase.prototype._resetMountPromise = function _resetMountPromise () {
this._mounted = false;
this._mountPromise = null;
this._resolveMountPromise = null;
this._rejectMountPromise = null;
const children = this._children;
for (let i=0, l=children.length; i<l; i+=1) {
children[i]._resetMountPromise();
}
};
ImperativeBase.prototype._needsToBeRendered = function _needsToBeRendered () {
if (this._awaitingMountPromiseToRender) { return Promise.resolve() }
const logic = () => {
this._willBeRendered = true;
Motor$1._setNodeToBeRendered(this);
};
if (!this._mounted) {
this._awaitingMountPromiseToRender = true;
let possibleError = undefined;
// try
return this.mountPromise
.then(logic)
// catch
.catch(() => {
if (e == 'mountcancel') { return }
else { possibleError = e; }
})
// finally
.then(() => {
this._awaitingMountPromiseToRender = false;
if (possibleError) { throw possibleError }
})
}
logic();
return Promise.resolve()
};
//async _needsToBeRendered() {
//if (this._awaitingMountPromiseToRender) return
//if (!this._mounted) {
//try {
//this._awaitingMountPromiseToRender = true
//await this.mountPromise
//} catch(e) {
//if (e == 'mountcancel') return
//else throw e
//} finally {
//this._awaitingMountPromiseToRender = false
//}
//}
//this._willBeRendered = true
//Motor._setNodeToBeRendered(this)
//}
// This method is used by Motor._renderNodes().
ImperativeBase.prototype._getAncestorToBeRendered = function _getAncestorToBeRendered () {
let parent = this._parent;
while (parent) {
if (parent._willBeRendered) { return parent }
parent = parent._parent;
}
return false
};
ImperativeBase.prototype._render = function _render (timestamp) {
ParentClass.prototype._render.call(this);
// applies the transform matrix to the element's style property.
this._elementManager.applyImperativeNodeProperties(this);
};
/**
* Set all properties of an ImperativeBase instance in one method.
*
* @param {Object} properties Properties object - see example.
*
* @example
* node.properties = {
* classes: ['open', 'big'],
* }
*/
prototypeAccessors.properties.set = function (properties) {
if ( properties === void 0 ) properties = {};
ParentClass.prototype.properties = properties;
if (properties.classes)
{ this._elementManager.setClasses(...properties.classes); }
};
Object.defineProperties( ImperativeBase.prototype, prototypeAccessors );
return ImperativeBase;
}(ParentClass));
Object.defineProperty(ImperativeBase, Symbol.hasInstance, {
value: function(obj) {
if (this !== ImperativeBase) { return Object.getPrototypeOf(ImperativeBase)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
ImperativeBase[instanceofSymbol] = true;
return ImperativeBase
};
ImperativeBase = ImperativeBaseMixin(Sizeable);
ImperativeBase.mixin = ImperativeBaseMixin;
}
initImperativeBase();
const instanceofSymbol = Symbol('instanceofSymbol');
let Node$1 = null;
const NodeMixin = base => {
var _Node = (function (superclass) {
function _Node (options) {
if ( options === void 0 ) options = {};
superclass.call(this, options);
// This was when using my `multiple()` implementation, we could call
// specific constructors using specific arguments. But, we're using
// class-factory style mixins for now, so we don't have control over the
// specific arguments we can pass to the constructors, so we're just
// using a single `options` parameter in all the constructors.
//this.callSuperConstructor(Transformable, options)
//this.callSuperConstructor(TreeNode)
//this.callSuperConstructor(ImperativeBase)
this._scene = null; // stores a ref to this Node's root Scene.
// This is an internal promise that resolves when this Node is added to
// to a scene graph that has a root Scene TreeNode. The resolved value
// is the root Scene.
this._scenePromise = null;
this._resolveScenePromise = null;
/**
* @private
* This method is defined here in the consructor as an arrow function
* because parent Nodes pass it to Observable#on and Observable#off. If
* it were a prototype method, then it would need to be bound when
* passed to Observable#on, which would require keeping track of the
* bound function reference in order to be able to pass it to
* Observable#off later. See ImperativeBase#add and
* ImperativeBase#remove.
*/
this._onParentSizeChange = () => {
// We only need to recalculate sizing and matrices if this node has
// properties that depend on parent sizing (proportional size,
// align, and mountPoint). mountPoint isn't obvious: if this node
// is proportionally sized, then the mountPoint will depend on the
// size of this element which depends on the size of this element's
// parent.
if (
this._properties.sizeMode.x === "proportional"
|| this._properties.sizeMode.y === "proportional"
|| this._properties.sizeMode.z === "proportional"
|| this._properties.align.x !== 0
|| this._properties.align.y !== 0
|| this._properties.align.z !== 0
) {
this._calcSize();
this._needsToBeRendered();
}
};
this._calcSize();
this._needsToBeRendered();
}
if ( superclass ) _Node.__proto__ = superclass;
_Node.prototype = Object.create( superclass && superclass.prototype );
_Node.prototype.constructor = _Node;
var prototypeAccessors = { scene: {} };
/**
* @private
*/
_Node.define = function define (name) {
customElements.define(name || 'i-node', Node$1);
};
_Node.prototype._waitForMountThenResolveMountPromise = function _waitForMountThenResolveMountPromise () {
if (this._awaitingScenePromise) { return Promise.resolve() }
const logic = () => {
this._mounted = true;
this._resolveMountPromise();
this._elementManager.shouldRender();
};
this._awaitingScenePromise = true;
let possibleError = undefined;
// try
return this._getScenePromise()
.then(() => this._scene.mountPromise)
.then(logic)
// catch
.catch(() => {
if (e == 'mountcancel') { return }
else { possibleError = e; }
})
// finally
.then(() => {
this._awaitingScenePromise = false;
if (possibleError) { throw possibleError }
})
};
//async _waitForMountThenResolveMountPromise() {
//if (this._awaitingScenePromise) return
//try {
//this._awaitingScenePromise = true
//await this._getScenePromise()
//await this._scene.mountPromise
//} catch (e) {
//if (e == 'mountcancel') return
//else throw e
//} finally {
//this._awaitingScenePromise = false
//}
//this._mounted = true
//this._resolveMountPromise()
//this._elementManager.shouldRender()
//}
/**
* @private
* Get a promise for the node's eventual scene.
*/
_Node.prototype._getScenePromise = function _getScenePromise () {
if (!this._scenePromise) {
this._scenePromise = new Promise((a, b) => {
this._resolveScenePromise = a;
});
}
if (this._scene)
{ this._resolveScenePromise(); }
return this._scenePromise
};
/**
* Get the Scene that this Node is in, null if no Scene. This is recursive
* at first, then cached.
*
* This traverses up the scene graph tree starting at this Node and finds
* the root Scene, if any. It caches the value for performance. If this
* Node is removed from a parent node with parent.remove(), then the
* cache is invalidated so the traversal can happen again when this Node is
* eventually added to a new tree. This way, if the scene is cached on a
* parent Node that we're adding this Node to then we can get that cached
* value instead of traversing the tree.
*
* @readonly
*/
prototypeAccessors.scene.get = function () {
// NOTE: this._scene is initally null, created in the constructor.
// if already cached, return it. Or if no parent, return it (it'll be null).
if (this._scene || !this._parent) { return this._scene }
// if the parent node already has a ref to the scene, use that.
if (this._parent._scene) {
this._scene = this._parent._scene;
}
else if (this._parent instanceof Scene) {
this._scene = this._parent;
}
// otherwise call the scene getter on the parent, which triggers
// traversal up the scene graph in order to find the root scene (null
// if none).
else {
this._scene = this._parent.scene;
}
return this._scene
};
/**
* @private
* This method to be called only when this Node has this.scene.
* Resolves the _scenePromise for all children of the tree of this Node.
*/
_Node.prototype._giveSceneRefToChildren = function _giveSceneRefToChildren () {
const children = this._children;
for (let i=0, l=children.length; i<l; i+=1) {
const childNode = children[i];
childNode._scene = this._scene;
if (childNode._resolveScenePromise)
{ childNode._resolveScenePromise(childNode._scene); }
childNode._giveSceneRefToChildren();
}
};
_Node.prototype._resetSceneRef = function _resetSceneRef () {
this._scene = null;
this._scenePromise = null;
this._resolveScenePromise = null;
const children = this._children;
for (let i=0, l=children.length; i<l; i+=1) {
children[i]._resetSceneRef();
}
};
Object.defineProperties( _Node.prototype, prototypeAccessors );
return _Node;
}(ImperativeBase.mixin(Transformable.mixin(base))));
Object.defineProperty(_Node, Symbol.hasInstance, {
value: function(obj) {
if (this !== _Node) { return Object.getPrototypeOf(_Node)[Symbol.hasInstance].call(this, obj) }
let currentProto = obj;
while(currentProto) {
const desc = Object.getOwnPropertyDescriptor(currentProto, "constructor");
if (desc && desc.value && desc.value.hasOwnProperty(instanceofSymbol))
{ return true }
currentProto = Object.getPrototypeOf(currentProto);
}
return false
}
});
_Node[instanceofSymbol] = true;
return _Node
};
Node$1 = NodeMixin((function () {
function anonymous () {}
return anonymous;
}()));
Node$1.mixin = NodeMixin;
// for now, hard-mixin the HTMLNode class. We'll do this automatically later.
Node$1 = Node$1.mixin(HTMLNode);
/**
* Manages a DOM element. Exposes a set of recommended APIs for working with
* DOM efficiently. Currently doesn't do much yet...
*/
var ElementManager = function ElementManager(element) {
this.element = element;
};
/**
* @param {Array.string} classes An array of class names to add to the
* managed element.
*
* Note: updating class names with `el.classList.add()` won't thrash the
* layout. See: http://www.html5rocks.com/en/tutorials/speed/animations
*/
ElementManager.prototype.setClasses = function setClasses (...classes) {
if (classes.length) { this.element.classList.add(...classes); }
return this
};
/**
* Apply a style property to the element.
*
* @private
* @param {string} property The CSS property we will a apply.
* @param {string} valueThe value the CSS property wil have.
*/
ElementManager.prototype.applyStyle = function applyStyle (property, value) {
this.element.style[property] = value;
};
ElementManager.prototype.add = function add (childElementManager) {
this.element.appendChild(childElementManager.element);
};
ElementManager.prototype.remove = function remove (childElementManager) {
// This conditional check is needed incase the element was already
// removed from the HTML-API side.
if (childElementManager.element.parentNode === this.element)
{ this.element.removeChild(childElementManager.element); }
};
ElementManager.prototype.connectChildElement = function connectChildElement (childImperativeNode) {
if (
// When using the imperative API, this statement is
// true, so the DOM elements need to be connected.
!childImperativeNode._elementManager.element.parentNode
// This condition is irrelevant when strictly using the
// imperative API. However, it is possible that when
// using the HTML API that the HTML-API node can be placed
// somewhere that isn't another HTML-API node, and the
// imperative Node can be gotten and used to add the
// node to another imperative Node. In this case, the
// HTML-API node will be added to the proper HTMLparent.
|| (childImperativeNode._elementManager.element.parentElement &&
childImperativeNode._elementManager.element.parentElement !== this.element)
// When an HTML-API node is already child of the
// relevant parent, or it is child of a shadow root of
// the relevant parent, there there's nothing to do,
// everything is already as expected, so the following
// conditional body is skipped.
) {
this.add(childImperativeNode._elementManager);
}
};
ElementManager.prototype.disconnectChildElement = function disconnectChildElement (childImperativeNode) {
// If DeclarativeBase#remove was called first, we don't need to
// call this again.
if (!childImperativeNode._elementManager.element.parentNode) { return }
this.remove(childImperativeNode._elementManager);
};
/**
* Apply the DOMMatrix value to the style of this Node's element.
*/
ElementManager.prototype.applyTransform = function applyTransform (domMatrix) {
// for now, template strings need to be on one line, otherwise Meteor
// users will have bugs from Meteor's injected line numbers. See:
// https://github.com/meteor/meteor/issues/9160
var cssMatrixString = `matrix3d( ${ domMatrix.m11 }, ${ domMatrix.m12 }, ${ domMatrix.m13 }, ${ domMatrix.m14 }, ${ domMatrix.m21 }, ${ domMatrix.m22 }, ${ domMatrix.m23 }, ${ domMatrix.m24 }, ${ domMatrix.m31 }, ${ domMatrix.m32 }, ${ domMatrix.m33 }, ${ domMatrix.m34 }, ${ domMatrix.m41 }, ${ domMatrix.m42 }, ${ domMatrix.m43 }, ${ domMatrix.m44 })`;
this.applyStyle('transform', cssMatrixString);
};
/**
* [applySize description]
*/
ElementManager.prototype.applySize = function applySize (size) {
var x = size.x;
var y = size.y;
this.applyStyle('width', `${x}px`);
this.applyStyle('height', `${y}px`);
// NOTE: we ignore the Z axis on elements, since they are flat.
};
ElementManager.prototype.applyOpacity = function applyOpacity (opacity) {
this.applyStyle('opacity', opacity);
};
ElementManager.prototype.applyImperativeNodeProperties = function applyImperativeNodeProperties (node) {
// Only Node is Transformable
if (node instanceof Node$1) {
this.applyOpacity(node._properties.opacity);
this.applyTransform(node._properties.transform);
}
// But both Node and Scene are Sizeable
this.applySize(node._calculatedSize);
};
ElementManager.prototype.shouldRender = function shouldRender () {
const task = Motor$1.addRenderTask(() => {
this.applyStyle('display', 'block');
Motor$1.removeRenderTask(task);
});
};
ElementManager.prototype.shouldNotRender = function shouldNotRender () {
const task = Motor$1.addRenderTask(() => {
this.applyStyle('display', 'none');
Motor$1.removeRenderTask(task);
});
};
var index$9 = Object.freeze({
ElementManager: ElementManager,
Motor: Motor$1,
get Node () { return Node$1; },
get Scene () { return Scene; },
Sizeable: Sizeable,
Transformable: Transformable,
TreeNode: TreeNode,
XYZValues: XYZValues,
Utility: Utility$2
});
var PushPaneLayout = (function (Node) {
function PushPaneLayout(...args) {
console.log(' -- PushPaneLayout created');
Node.call(this, ...args);
}
if ( Node ) PushPaneLayout.__proto__ = Node;
PushPaneLayout.prototype = Object.create( Node && Node.prototype );
PushPaneLayout.prototype.constructor = PushPaneLayout;
return PushPaneLayout;
}(Node$1));
var HTMLPushPaneLayout = (function (HTMLNode$$1) {
function HTMLPushPaneLayout () {
HTMLNode$$1.apply(this, arguments);
}
if ( HTMLNode$$1 ) HTMLPushPaneLayout.__proto__ = HTMLNode$$1;
HTMLPushPaneLayout.prototype = Object.create( HTMLNode$$1 && HTMLNode$$1.prototype );
HTMLPushPaneLayout.prototype.constructor = HTMLPushPaneLayout;
HTMLPushPaneLayout.define = function define (name) {
customElements.define(name || 'i-push-pane-layout', HTMLPushPaneLayout);
};
// @override
HTMLPushPaneLayout.prototype._makeImperativeCounterpart = function _makeImperativeCounterpart () {
return new PushPaneLayout({}, this)
};
return HTMLPushPaneLayout;
}(HTMLNode));
(function(){
'use strict';var h=new function(){};var aa=new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));function m(b){var a=aa.has(b);b=/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(b);return!a&&b}function n(b){var a=b.isConnected;if(void 0!==a){ return a; }for(;b&&!(b.__CE_isImportDocument||b instanceof Document);){ b=b.parentNode||(window.ShadowRoot&&b instanceof ShadowRoot?b.host:void 0); }return!(!b||!(b.__CE_isImportDocument||b instanceof Document))}
function p(b,a){for(;a&&a!==b&&!a.nextSibling;){ a=a.parentNode; }return a&&a!==b?a.nextSibling:null}
function t(b,a,d){d=d?d:new Set;for(var c=b;c;){if(c.nodeType===Node.ELEMENT_NODE){var e=c;a(e);var f=e.localName;if("link"===f&&"import"===e.getAttribute("rel")){c=e.import;if(c instanceof Node&&!d.has(c)){ for(d.add(c),c=c.firstChild;c;c=c.nextSibling){ t(c,a,d); } }c=p(b,e);continue}else if("template"===f){c=p(b,e);continue}if(e=e.__CE_shadowRoot){ for(e=e.firstChild;e;e=e.nextSibling){ t(e,a,d); } }}c=c.firstChild?c.firstChild:p(b,c);}}function u(b,a,d){b[a]=d;}function v(){this.a=new Map;this.o=new Map;this.f=[];this.b=!1;}function ba(b,a,d){b.a.set(a,d);b.o.set(d.constructor,d);}function w(b,a){b.b=!0;b.f.push(a);}function x(b,a){b.b&&t(a,function(a){return y(b,a)});}function y(b,a){if(b.b&&!a.__CE_patched){a.__CE_patched=!0;for(var d=0;d<b.f.length;d++){ b.f[d](a); }}}function z(b,a){var d=[];t(a,function(b){return d.push(b)});for(a=0;a<d.length;a++){var c=d[a];1===c.__CE_state?b.connectedCallback(c):A(b,c);}}
function B(b,a){var d=[];t(a,function(b){return d.push(b)});for(a=0;a<d.length;a++){var c=d[a];1===c.__CE_state&&b.disconnectedCallback(c);}}
function C(b,a,d){d=d?d:{};var c=d.w||new Set,e=d.s||function(a){return A(b,a)},f=[];t(a,function(a){if("link"===a.localName&&"import"===a.getAttribute("rel")){var d=a.import;d instanceof Node&&"complete"===d.readyState?(d.__CE_isImportDocument=!0,d.__CE_hasRegistry=!0):a.addEventListener("load",function(){var d=a.import;if(!d.__CE_documentLoadHandled){d.__CE_documentLoadHandled=!0;d.__CE_isImportDocument=!0;d.__CE_hasRegistry=!0;var f=new Set(c);f.delete(d);C(b,d,{w:f,s:e});}});}else { f.push(a); }},c);
if(b.b){ for(a=0;a<f.length;a++){ y(b,f[a]); } }for(a=0;a<f.length;a++){ e(f[a]); }}
function A(b,a){if(void 0===a.__CE_state){var d=b.a.get(a.localName);if(d){d.constructionStack.push(a);var c=d.constructor;try{try{if(new c!==a){ throw Error("The custom element constructor did not produce the element being upgraded."); }}finally{d.constructionStack.pop();}}catch(r){throw a.__CE_state=2,r;}a.__CE_state=1;a.__CE_definition=d;if(d.attributeChangedCallback){ for(d=d.observedAttributes,c=0;c<d.length;c++){var e=d[c],f=a.getAttribute(e);null!==f&&b.attributeChangedCallback(a,e,null,f,null);} }n(a)&&
b.connectedCallback(a);}}}v.prototype.connectedCallback=function(b){var a=b.__CE_definition;a.connectedCallback&&a.connectedCallback.call(b);};v.prototype.disconnectedCallback=function(b){var a=b.__CE_definition;a.disconnectedCallback&&a.disconnectedCallback.call(b);};v.prototype.attributeChangedCallback=function(b,a,d,c,e){var f=b.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(a)&&f.attributeChangedCallback.call(b,a,d,c,e);};function D(b,a){this.c=b;this.a=a;this.b=void 0;C(this.c,this.a);"loading"===this.a.readyState&&(this.b=new MutationObserver(this.f.bind(this)),this.b.observe(this.a,{childList:!0,subtree:!0}));}function E(b){b.b&&b.b.disconnect();}D.prototype.f=function(b){var a=this.a.readyState;"interactive"!==a&&"complete"!==a||E(this);for(a=0;a<b.length;a++){ for(var d=b[a].addedNodes,c=0;c<d.length;c++){ C(this.c,d[c]); } }};function ca(){var b=this;this.b=this.a=void 0;this.f=new Promise(function(a){b.b=a;b.a&&a(b.a);});}function F(b){if(b.a){ throw Error("Already resolved."); }b.a=void 0;b.b&&b.b(void 0);}function G(b){this.i=!1;this.c=b;this.m=new Map;this.j=function(b){return b()};this.g=!1;this.l=[];this.u=new D(b,document);}
G.prototype.define=function(b,a){var d=this;if(!(a instanceof Function)){ throw new TypeError("Custom element constructors must be functions."); }if(!m(b)){ throw new SyntaxError("The element name '"+b+"' is not valid."); }if(this.c.a.get(b)){ throw Error("A custom element with name '"+b+"' has already been defined."); }if(this.i){ throw Error("A custom element is already being defined."); }this.i=!0;var c,e,f,r,k;try{var g=function(b){var a=l[b];if(void 0!==a&&!(a instanceof Function)){ throw Error("The '"+b+"' callback must be a function."); }
return a},l=a.prototype;if(!(l instanceof Object)){ throw new TypeError("The custom element constructor's prototype is not an object."); }c=g("connectedCallback");e=g("disconnectedCallback");f=g("adoptedCallback");r=g("attributeChangedCallback");k=a.observedAttributes||[];}catch(q){return}finally{this.i=!1;}a={localName:b,constructor:a,connectedCallback:c,disconnectedCallback:e,adoptedCallback:f,attributeChangedCallback:r,observedAttributes:k,constructionStack:[]};ba(this.c,b,a);this.l.push(a);this.g||
(this.g=!0,this.j(function(){return da(d)}));};function da(b){if(!1!==b.g){b.g=!1;for(var a=b.l,d=[],c=new Map,e=0;e<a.length;e++){ c.set(a[e].localName,[]); }C(b.c,document,{s:function(a){if(void 0===a.__CE_state){var e=a.localName,f=c.get(e);f?f.push(a):b.c.a.get(e)&&d.push(a);}}});for(e=0;e<d.length;e++){ A(b.c,d[e]); }for(;0<a.length;){for(var f=a.shift(),e=f.localName,f=c.get(f.localName),r=0;r<f.length;r++){ A(b.c,f[r]); }(e=b.m.get(e))&&F(e);}}}G.prototype.get=function(b){if(b=this.c.a.get(b)){ return b.constructor }};
G.prototype.whenDefined=function(b){if(!m(b)){ return Promise.reject(new SyntaxError("'"+b+"' is not a valid custom element name.")); }var a=this.m.get(b);if(a){ return a.f; }a=new ca;this.m.set(b,a);this.c.a.get(b)&&!this.l.some(function(a){return a.localName===b})&&F(a);return a.f};G.prototype.v=function(b){E(this.u);var a=this.j;this.j=function(d){return b(function(){return a(d)})};};window.CustomElementRegistry=G;G.prototype.define=G.prototype.define;G.prototype.get=G.prototype.get;
G.prototype.whenDefined=G.prototype.whenDefined;G.prototype.polyfillWrapFlushCallback=G.prototype.v;var H=window.Document.prototype.createElement,ea=window.Document.prototype.createElementNS,fa=window.Document.prototype.importNode,ga=window.Document.prototype.prepend,ha=window.Document.prototype.append,ia=window.DocumentFragment.prototype.prepend,ja=window.DocumentFragment.prototype.append,I=window.Node.prototype.cloneNode,J=window.Node.prototype.appendChild,K=window.Node.prototype.insertBefore,L=window.Node.prototype.removeChild,M=window.Node.prototype.replaceChild,N=Object.getOwnPropertyDescriptor(window.Node.prototype,
"textContent"),O=window.Element.prototype.attachShadow,P=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),Q=window.Element.prototype.getAttribute,R=window.Element.prototype.setAttribute,S=window.Element.prototype.removeAttribute,T=window.Element.prototype.getAttributeNS,U=window.Element.prototype.setAttributeNS,ka=window.Element.prototype.removeAttributeNS,la=window.Element.prototype.insertAdjacentElement,ma=window.Element.prototype.prepend,na=window.Element.prototype.append,
V=window.Element.prototype.before,oa=window.Element.prototype.after,pa=window.Element.prototype.replaceWith,qa=window.Element.prototype.remove,ra=window.HTMLElement,W=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),sa=window.HTMLElement.prototype.insertAdjacentElement;function ta(){var b=X;window.HTMLElement=function(){function a(){var a=this.constructor,c=b.o.get(a);if(!c){ throw Error("The custom element being constructed was not registered with `customElements`."); }var e=c.constructionStack;if(!e.length){ return e=H.call(document,c.localName),Object.setPrototypeOf(e,a.prototype),e.__CE_state=1,e.__CE_definition=c,y(b,e),e; }var c=e.length-1,f=e[c];if(f===h){ throw Error("The HTMLElement constructor was either called reentrantly for this constructor or called multiple times."); }
e[c]=h;Object.setPrototypeOf(f,a.prototype);y(b,f);return f}a.prototype=ra.prototype;return a}();}function Y(b,a,d){function c(a){return function(d){for(var e=[],c=0;c<arguments.length;++c){ e[c-0]=arguments[c]; }for(var c=[],f=[],l=0;l<e.length;l++){var q=e[l];q instanceof Element&&n(q)&&f.push(q);if(q instanceof DocumentFragment){ for(q=q.firstChild;q;q=q.nextSibling){ c.push(q); } }else { c.push(q); }}a.apply(this,e);for(e=0;e<f.length;e++){ B(b,f[e]); }if(n(this)){ for(e=0;e<c.length;e++){ f=c[e],f instanceof Element&&z(b,f); } }}}d.h&&(a.prepend=c(d.h));d.append&&(a.append=c(d.append));}function ua(){var b=X;u(Document.prototype,"createElement",function(a){if(this.__CE_hasRegistry){var d=b.a.get(a);if(d){ return new d.constructor }}a=H.call(this,a);y(b,a);return a});u(Document.prototype,"importNode",function(a,d){a=fa.call(this,a,d);this.__CE_hasRegistry?C(b,a):x(b,a);return a});u(Document.prototype,"createElementNS",function(a,d){if(this.__CE_hasRegistry&&(null===a||"http://www.w3.org/1999/xhtml"===a)){var c=b.a.get(d);if(c){ return new c.constructor }}a=ea.call(this,a,d);y(b,a);return a});
Y(b,Document.prototype,{h:ga,append:ha});}function va(){var b=X;function a(a,c){Object.defineProperty(a,"textContent",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(a){if(this.nodeType===Node.TEXT_NODE){ c.set.call(this,a); }else{var e=void 0;if(this.firstChild){var d=this.childNodes,k=d.length;if(0<k&&n(this)){ for(var e=Array(k),g=0;g<k;g++){ e[g]=d[g]; } }}c.set.call(this,a);if(e){ for(a=0;a<e.length;a++){ B(b,e[a]); } }}}});}u(Node.prototype,"insertBefore",function(a,c){if(a instanceof DocumentFragment){var e=Array.prototype.slice.apply(a.childNodes);
a=K.call(this,a,c);if(n(this)){ for(c=0;c<e.length;c++){ z(b,e[c]); } }return a}e=n(a);c=K.call(this,a,c);e&&B(b,a);n(this)&&z(b,a);return c});u(Node.prototype,"appendChild",function(a){if(a instanceof DocumentFragment){var c=Array.prototype.slice.apply(a.childNodes);a=J.call(this,a);if(n(this)){ for(var e=0;e<c.length;e++){ z(b,c[e]); } }return a}c=n(a);e=J.call(this,a);c&&B(b,a);n(this)&&z(b,a);return e});u(Node.prototype,"cloneNode",function(a){a=I.call(this,a);this.ownerDocument.__CE_hasRegistry?C(b,a):x(b,a);
return a});u(Node.prototype,"removeChild",function(a){var c=n(a),e=L.call(this,a);c&&B(b,a);return e});u(Node.prototype,"replaceChild",function(a,c){if(a instanceof DocumentFragment){var e=Array.prototype.slice.apply(a.childNodes);a=M.call(this,a,c);if(n(this)){ for(B(b,c),c=0;c<e.length;c++){ z(b,e[c]); } }return a}var e=n(a),f=M.call(this,a,c),d=n(this);d&&B(b,c);e&&B(b,a);d&&z(b,a);return f});N&&N.get?a(Node.prototype,N):w(b,function(b){a(b,{enumerable:!0,configurable:!0,get:function(){for(var a=[],b=
0;b<this.childNodes.length;b++){ a.push(this.childNodes[b].textContent); }return a.join("")},set:function(a){for(;this.firstChild;){ L.call(this,this.firstChild); }J.call(this,document.createTextNode(a));}});});}function wa(b){var a=Element.prototype;function d(a){return function(e){for(var c=[],d=0;d<arguments.length;++d){ c[d-0]=arguments[d]; }for(var d=[],k=[],g=0;g<c.length;g++){var l=c[g];l instanceof Element&&n(l)&&k.push(l);if(l instanceof DocumentFragment){ for(l=l.firstChild;l;l=l.nextSibling){ d.push(l); } }else { d.push(l); }}a.apply(this,c);for(c=0;c<k.length;c++){ B(b,k[c]); }if(n(this)){ for(c=0;c<d.length;c++){ k=d[c],k instanceof Element&&z(b,k); } }}}V&&(a.before=d(V));V&&(a.after=d(oa));pa&&u(a,"replaceWith",function(a){for(var e=
[],c=0;c<arguments.length;++c){ e[c-0]=arguments[c]; }for(var c=[],d=[],k=0;k<e.length;k++){var g=e[k];g instanceof Element&&n(g)&&d.push(g);if(g instanceof DocumentFragment){ for(g=g.firstChild;g;g=g.nextSibling){ c.push(g); } }else { c.push(g); }}k=n(this);pa.apply(this,e);for(e=0;e<d.length;e++){ B(b,d[e]); }if(k){ for(B(b,this),e=0;e<c.length;e++){ d=c[e],d instanceof Element&&z(b,d); } }});qa&&u(a,"remove",function(){var a=n(this);qa.call(this);a&&B(b,this);});}function xa(){var b=X;function a(a,c){Object.defineProperty(a,"innerHTML",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(a){var e=this,d=void 0;n(this)&&(d=[],t(this,function(a){a!==e&&d.push(a);}));c.set.call(this,a);if(d){ for(var f=0;f<d.length;f++){var r=d[f];1===r.__CE_state&&b.disconnectedCallback(r);} }this.ownerDocument.__CE_hasRegistry?C(b,this):x(b,this);return a}});}function d(a,c){u(a,"insertAdjacentElement",function(a,e){var d=n(e);a=c.call(this,a,e);d&&B(b,e);n(a)&&z(b,e);
return a});}O&&u(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=O.call(this,a)});if(P&&P.get){ a(Element.prototype,P); }else if(W&&W.get){ a(HTMLElement.prototype,W); }else{var c=H.call(document,"div");w(b,function(b){a(b,{enumerable:!0,configurable:!0,get:function(){return I.call(this,!0).innerHTML},set:function(a){var b="template"===this.localName?this.content:this;for(c.innerHTML=a;0<b.childNodes.length;){ L.call(b,b.childNodes[0]); }for(;0<c.childNodes.length;){ J.call(b,c.childNodes[0]); }}});});}u(Element.prototype,
"setAttribute",function(a,c){if(1!==this.__CE_state){ return R.call(this,a,c); }var e=Q.call(this,a);R.call(this,a,c);c=Q.call(this,a);b.attributeChangedCallback(this,a,e,c,null);});u(Element.prototype,"setAttributeNS",function(a,c,d){if(1!==this.__CE_state){ return U.call(this,a,c,d); }var e=T.call(this,a,c);U.call(this,a,c,d);d=T.call(this,a,c);b.attributeChangedCallback(this,c,e,d,a);});u(Element.prototype,"removeAttribute",function(a){if(1!==this.__CE_state){ return S.call(this,a); }var c=Q.call(this,a);S.call(this,
a);null!==c&&b.attributeChangedCallback(this,a,c,null,null);});u(Element.prototype,"removeAttributeNS",function(a,c){if(1!==this.__CE_state){ return ka.call(this,a,c); }var d=T.call(this,a,c);ka.call(this,a,c);var e=T.call(this,a,c);d!==e&&b.attributeChangedCallback(this,c,d,e,a);});sa?d(HTMLElement.prototype,sa):la?d(Element.prototype,la):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");Y(b,Element.prototype,{h:ma,append:na});wa(b);}/*
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
var Z=window.customElements;if(!Z||Z.forcePolyfill||"function"!=typeof Z.define||"function"!=typeof Z.get){var X=new v;ta();ua();Y(X,DocumentFragment.prototype,{h:ia,append:ja});va();xa();document.__CE_hasRegistry=!0;var customElements=new G(X);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:customElements});}
}).call(self);
//import 'document-register-element'
function useDefaultNames() {
if (!customElements.get('i-node')) { Node$1.define(); }
if (!customElements.get('i-scene')) { Scene.define(); }
//PushPaneLayout.define()
}
var index$12 = Object.freeze({
get DeclarativeBase () { return DeclarativeBase; },
HTMLNode: HTMLNode,
HTMLPushPaneLayout: HTMLPushPaneLayout,
HTMLScene: HTMLScene,
WebComponent: WebComponentMixin,
useDefaultNames: useDefaultNames
});
/*
* LICENSE
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
/**
* A scenegraph tree that lays things out in a cube form.
*
* XXX: Rename to CubeLayout?
*
* @class Cube
* @extends Node
*/
var Cube$1 = (function (Node) {
function Cube(size, options) {
// cubes, the same size on all sides
Node.call(this, Object.assign({}, {absoluteSize: [size, size, size]}, options));
//GenericSync.register({
//mouse: MouseSync,
//touch: TouchSync
//});
this.size = size;
this.sides = [];
forLength(6, n => this._createCubeSide(n));
}
if ( Node ) Cube.__proto__ = Node;
Cube.prototype = Object.create( Node && Node.prototype );
Cube.prototype.constructor = Cube;
/**
* Creates the 6 sides of the cube (the leafnodes of the scenegraph).
*
* @private
* @param {Number} index The index (a integer between 0 and 5) that specifies which side to create.
*/
Cube.prototype._createCubeSide = function _createCubeSide (index) {
const rotator = new Node({
align: [0.5, 0.5],
mountPoint: [0.5, 0.5],
});
const side = new Node({
align: [0.5, 0.5],
mountPoint: [0.5, 0.5],
absoluteSize: [this.size, this.size],
});
this.sides.push(side);
rotator.add(side);
// XXX: make a new GenericSync-like thing?
//const sync = new GenericSync(['mouse','touch']);
//side.pipe(sync);
//sync.pipe(this.options.handler);
// rotate and place each side.
if (index < 4) // 4 sides
{ rotator.rotation.y = 90 * index; }
else // top/bottom
{ rotator.rotation.x = 90 * ( index % 2 ? -1 : 1 ); }
side.position.z = this.size / 2;
this.add(rotator);
};
/**
* Set the content for the sides of the cube.
*
* @param {Array} content An array containing [Node](#infamous/motor/Node)
* instances to place in the cube sides. Only the first 6 items are used,
* the rest are ignored.
*/
Cube.prototype.setContent = function setContent (content) {
forLength(6, index => {
//this.cubeSideNodes[index].set(null); // TODO: how do we erase previous content?
this.sides[index].add(content[index]);
});
return this;
};
return Cube;
}(Node$1));
var index$13 = Object.freeze({
Cube: Cube$1,
PushPaneLayout: PushPaneLayout
});
const version = '17.0.5';
exports.Calendar = Calendar;
exports.DoubleSidedPlane = DoubleSidedPlane;
exports.Grid = Grid;
exports.Molecule = Molecule;
exports.Plane = Plane;
exports.PushMenuLayout = PushMenuLayout;
exports.utils = utils;
exports.core = index$9;
exports.html = index$12;
exports.components = index$13;
exports.version = version;
return exports;
}({}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment