Created
July 17, 2014 13:15
-
-
Save markmarijnissen/8d27432be589f9f2115e to your computer and use it in GitHub Desktop.
Famo.us in one file (including css). Version 0.2.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# set famous version here | |
VERSION=0.2 | |
# | |
echo "// Famo.us in one file. Including CSS. Version $VERSION." > famousBox.js | |
# load all libraries | |
curl -s http://code.famo.us/lib/functionPrototypeBind.js >> famousBox.js | |
curl -s http://code.famo.us/lib/classList.js >> famousBox.js | |
curl -s http://code.famo.us/lib/requestAnimationFrame.js >> famousBox.js | |
curl -s http://code.famo.us/lib/require.js >> famousBox.js | |
# Famo.us | |
curl -s http://code.famo.us/famous/$VERSION/famous.js >> famousBox.js | |
# Load snippet | |
echo "(function(){ | |
function loadcss(filename) { | |
var fileref = document.createElement('link'); | |
fileref.setAttribute('rel', 'stylesheet'); | |
fileref.setAttribute('type', 'text/css'); | |
fileref.setAttribute('href', filename); | |
document.getElementsByTagName('head')[0].appendChild(fileref); | |
} | |
loadcss('http://code.famo.us/famous/$VERSION/famous.css'); | |
window.addEventListener('load',function(){ | |
console.log('----------- start -----------'); | |
require(['main']); | |
}); | |
})();" >> famousBox.js | |
hash uglifyjs 2>/dev/null || { echo >&2 "Type 'npm install -g uglify-js' to minify famousBox.js!"; exit 1; } | |
uglifyjs famousBox.js -o famousBox.min.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Famo.us in one file. Including CSS. Version 0.2. | |
if (!Function.prototype.bind) { | |
Function.prototype.bind = function (oThis) { | |
if (typeof this !== "function") { | |
// closest thing possible to the ECMAScript 5 internal IsCallable function | |
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); | |
} | |
var aArgs = Array.prototype.slice.call(arguments, 1), | |
fToBind = this, | |
fNOP = function () {}, | |
fBound = function () { | |
return fToBind.apply(this instanceof fNOP && oThis | |
? this | |
: oThis, | |
aArgs.concat(Array.prototype.slice.call(arguments))); | |
}; | |
fNOP.prototype = this.prototype; | |
fBound.prototype = new fNOP(); | |
return fBound; | |
}; | |
} | |
/* | |
* classList.js: Cross-browser full element.classList implementation. | |
* 2011-06-15 | |
* | |
* By Eli Grey, http://eligrey.com | |
* Public Domain. | |
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
*/ | |
/*global self, document, DOMException */ | |
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ | |
if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { | |
(function (view) { | |
"use strict"; | |
var | |
classListProp = "classList" | |
, protoProp = "prototype" | |
, elemCtrProto = (view.HTMLElement || view.Element)[protoProp] | |
, objCtr = Object | |
, strTrim = String[protoProp].trim || function () { | |
return this.replace(/^\s+|\s+$/g, ""); | |
} | |
, arrIndexOf = Array[protoProp].indexOf || function (item) { | |
var | |
i = 0 | |
, len = this.length | |
; | |
for (; i < len; i++) { | |
if (i in this && this[i] === item) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
// Vendors: please allow content code to instantiate DOMExceptions | |
, DOMEx = function (type, message) { | |
this.name = type; | |
this.code = DOMException[type]; | |
this.message = message; | |
} | |
, checkTokenAndGetIndex = function (classList, token) { | |
if (token === "") { | |
throw new DOMEx( | |
"SYNTAX_ERR" | |
, "An invalid or illegal string was specified" | |
); | |
} | |
if (/\s/.test(token)) { | |
throw new DOMEx( | |
"INVALID_CHARACTER_ERR" | |
, "String contains an invalid character" | |
); | |
} | |
return arrIndexOf.call(classList, token); | |
} | |
, ClassList = function (elem) { | |
var | |
trimmedClasses = strTrim.call(elem.className) | |
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] | |
, i = 0 | |
, len = classes.length | |
; | |
for (; i < len; i++) { | |
this.push(classes[i]); | |
} | |
this._updateClassName = function () { | |
elem.className = this.toString(); | |
}; | |
} | |
, classListProto = ClassList[protoProp] = [] | |
, classListGetter = function () { | |
return new ClassList(this); | |
} | |
; | |
// Most DOMException implementations don't allow calling DOMException's toString() | |
// on non-DOMExceptions. Error's toString() is sufficient here. | |
DOMEx[protoProp] = Error[protoProp]; | |
classListProto.item = function (i) { | |
return this[i] || null; | |
}; | |
classListProto.contains = function (token) { | |
token += ""; | |
return checkTokenAndGetIndex(this, token) !== -1; | |
}; | |
classListProto.add = function (token) { | |
token += ""; | |
if (checkTokenAndGetIndex(this, token) === -1) { | |
this.push(token); | |
this._updateClassName(); | |
} | |
}; | |
classListProto.remove = function (token) { | |
token += ""; | |
var index = checkTokenAndGetIndex(this, token); | |
if (index !== -1) { | |
this.splice(index, 1); | |
this._updateClassName(); | |
} | |
}; | |
classListProto.toggle = function (token) { | |
token += ""; | |
if (checkTokenAndGetIndex(this, token) === -1) { | |
this.add(token); | |
} else { | |
this.remove(token); | |
} | |
}; | |
classListProto.toString = function () { | |
return this.join(" "); | |
}; | |
if (objCtr.defineProperty) { | |
var classListPropDesc = { | |
get: classListGetter | |
, enumerable: true | |
, configurable: true | |
}; | |
try { | |
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); | |
} catch (ex) { // IE 8 doesn't support enumerable:true | |
if (ex.number === -0x7FF5EC54) { | |
classListPropDesc.enumerable = false; | |
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); | |
} | |
} | |
} else if (objCtr[protoProp].__defineGetter__) { | |
elemCtrProto.__defineGetter__(classListProp, classListGetter); | |
} | |
}(self)); | |
} | |
// adds requestAnimationFrame functionality | |
// Source: http://strd6.com/2011/05/better-window-requestanimationframe-shim/ | |
window.requestAnimationFrame || (window.requestAnimationFrame = | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback, element) { | |
return window.setTimeout(function() { | |
callback(+new Date()); | |
}, 1000 / 60); | |
}); | |
/* | |
RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. | |
Available via the MIT or new BSD license. | |
see: http://github.com/jrburke/requirejs for details | |
*/ | |
var requirejs,require,define; | |
(function(ca){function G(b){return"[object Function]"===M.call(b)}function H(b){return"[object Array]"===M.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof | |
RegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b;}function ea(b){if(!b)return b;var c=ca;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split("/");n=I;var m=l.map,k=m&&m["*"];if(a&&"."===a.charAt(0))if(e){n= | |
I.slice(0,I.length-1);a=a.split("/");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,""));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],"."===c)n.splice(e,1),e-=1;else if(".."===c)if(1===e&&(".."===n[2]||".."===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split("/");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join("/");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join("/")))if(b=j(b,d)){f=b; | |
g=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join("/"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(e){if(e.getAttribute("data-requiremodule")===a&&e.getAttribute("data-requirecontext")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf("!"):-1;-1<b&&(e=a.substring(0, | |
b),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k="";a||(m=!1,a="_@r"+(M+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?"_unnormalized"+(Q+=1):"";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+"!"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a)); | |
return b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))"defined"===e&&b(p[f]);else if(n=q(a),n.error&&"error"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit("error",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit("error",a.error):(e[f]= | |
!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1E3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C("timeout","Load timeout for modules: "+ | |
f,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function K(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent("onreadystatechange",e):a.removeEventListener("load",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener("error",e,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function L(){var a; | |
for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var X,$,i,N,Y,l={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},M=1,Q=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?p[a.map.id]=a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module? | |
a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on("error",b);else this.events.error&&(b=t(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback= | |
b;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a= | |
this.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&& | |
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= | |
this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f); | |
if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval", | |
"fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b, | |
a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m, | |
nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b, | |
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild= | |
!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!== | |
d&&(!("."===g||".."===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b, | |
c,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||"");else{f=l.paths;a=a.split("/");for(d=a.length;0<d;d-=1)if(g=a.slice(0, | |
d).join("/"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join("/");f+=b||(/^data\:|\?/.test(f)||c?"":".js");f=("/"===f.charAt(0)||f.match(/^[\w\+\.\-]+:/)?"":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf("?")?"?":"&")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=K(a),i.completeLoad(a.id)},onScriptError:function(a){var b=K(a);if(!g(b.id))return w(C("scripterror", | |
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,K,E,P,L,q,Q,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,R=/\.js$/,ja=/^\.\//;x=Object.prototype;var M=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),fa=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/, | |
Z="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},r={},S=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m="_";!H(b)&&"string"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)}; | |
h.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version="2.1.11";h.jsExtRegExp=/^\/|:|\?|\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v(["toUrl","undef","defined","specified"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c= | |
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute("data-requirecontext",b.contextName),g.setAttribute("data-requiremodule",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): | |
(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl= | |
Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b|| | |
(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Entity',['require','exports','module'],function(require, exports, module) { | |
/** | |
* 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 reigstration 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 reigstration id | |
* @return {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 reigstration id | |
*/ | |
function unregister(id) { | |
set(id, null); | |
} | |
module.exports = { | |
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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Transform',['require','exports','module'],function(require, exports, module) { | |
/** | |
* 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 or more 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 atrix 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; | |
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, 0, 0, 0, Math.tan(psi), 1, 0, 0, Math.tan(theta), 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]; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/SpecParser',['require','exports','module','./Transform'],function(require, exports, module) { | |
var Transform = require('./Transform'); | |
/** | |
* | |
* 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.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 _originZeroZero = [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 || parentContext.origin; | |
if (parentContext.size && align && (align[0] || align[1])) { | |
var alignAdjust = [align[0] * parentContext.size[0], align[1] * parentContext.size[1], 0]; | |
transform = Transform.thenMove(transform, _vecInContext(alignAdjust, sizeContext)); | |
} | |
this.result[id] = { | |
transform: transform, | |
opacity: parentContext.opacity, | |
origin: parentContext.origin || _originZeroZero, | |
align: parentContext.align || parentContext.origin || _originZeroZero, | |
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.multiply(parentContext.transform, spec.transform); | |
if (spec.origin) { | |
origin = spec.origin; | |
nextSizeContext = parentContext.transform; | |
} | |
if (spec.align) align = spec.align; | |
if (spec.size) { | |
var parentSize = parentContext.size; | |
size = [ | |
spec.size[0] !== undefined ? spec.size[0] : parentSize[0], | |
spec.size[1] !== undefined ? spec.size[1] : parentSize[1] | |
]; | |
if (parentSize) { | |
if (!align) align = origin; | |
if (align && (align[0] || align[1])) transform = Transform.thenMove(transform, _vecInContext([align[0] * parentSize[0], align[1] * parentSize[1], 0], sizeContext)); | |
if (origin && (origin[0] || origin[1])) transform = Transform.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); | |
} | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/RenderNode',['require','exports','module','./Entity','./SpecParser'],function(require, exports, module) { | |
var Entity = require('./Entity'); | |
var SpecParser = require('./SpecParser'); | |
/** | |
* 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.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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/EventEmitter',['require','exports','module'],function(require, exports, module) { | |
/** | |
* 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 index = this.listeners[type].indexOf(handler); | |
if (index >= 0) this.listeners[type].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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/EventHandler',['require','exports','module','./EventEmitter'],function(require, exports, module) { | |
var EventEmitter = require('./EventEmitter'); | |
/** | |
* 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.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.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.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.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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/ElementAllocator',['require','exports','module'],function(require, exports, module) { | |
/** | |
* 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.removeChild(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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/utilities/Utility',['require','exports','module'],function(require, exports, module) { | |
/** | |
* 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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/MultipleTransition',['require','exports','module','famous/utilities/Utility'],function(require, exports, module) { | |
var Utility = require('famous/utilities/Utility'); | |
/** | |
* 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.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]); | |
} | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/TweenTransition',['require','exports','module'],function(require, exports, module) { | |
/** | |
* | |
* 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; | |
}; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/Transitionable',['require','exports','module','./MultipleTransition','./TweenTransition'],function(require, exports, module) { | |
var MultipleTransition = require('./MultipleTransition'); | |
var TweenTransition = require('./TweenTransition'); | |
/** | |
* 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.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; | |
} | |
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(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) { | |
this.set(this.get(), {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() { | |
this.set(this.get()); | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Context',['require','exports','module','./RenderNode','./EventHandler','./ElementAllocator','./Transform','famous/transitions/Transitionable'],function(require, exports, module) { | |
var RenderNode = require('./RenderNode'); | |
var EventHandler = require('./EventHandler'); | |
var ElementAllocator = require('./ElementAllocator'); | |
var Transform = require('./Transform'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var _originZeroZero = [0, 0]; | |
function _getElementSize(element) { | |
return [element.clientWidth, element.clientHeight]; | |
} | |
/** | |
* 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(container); | |
this._node = new RenderNode(); | |
this._eventOutput = new EventHandler(); | |
this._size = _getElementSize(this.container); | |
this._perspectiveState = new Transitionable(0); | |
this._perspective = undefined; | |
this._nodeContext = { | |
allocator: this._allocator, | |
transform: Transform.identity, | |
opacity: 1, | |
origin: _originZeroZero, | |
align: null, | |
size: this._size | |
}; | |
this._eventOutput.on('resize', function() { | |
this.setSize(_getElementSize(this.container)); | |
}.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(this.container); | |
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) { | |
this.container.style.perspective = perspective ? perspective.toFixed() + 'px' : ''; | |
this.container.style.webkitPerspective = perspective ? perspective.toFixed() : ''; | |
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); | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/OptionsManager',['require','exports','module','./EventHandler'],function(require, exports, module) { | |
var EventHandler = require('./EventHandler'); | |
/** | |
* 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(); | |
this.eventOutput.bindThis(this); | |
EventHandler.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 | |
* @method get | |
* | |
* @param {string} key key | |
* @return {Object} associated object | |
*/ | |
OptionsManager.prototype.get = function get(key) { | |
return this._value[key]; | |
}; | |
/** | |
* 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; | |
}; | |
/** | |
* Return entire object contents of this OptionsManager. | |
* | |
* @method value | |
* | |
* @return {Object} current state of options | |
*/ | |
OptionsManager.prototype.value = function value() { | |
return this._value; | |
}; | |
/** | |
* 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); | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Engine',['require','exports','module','./Context','./EventHandler','./OptionsManager'],function(require, exports, module) { | |
/** | |
* 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 Context = require('./Context'); | |
var EventHandler = require('./EventHandler'); | |
var OptionsManager = require('./OptionsManager'); | |
var Engine = {}; | |
var contexts = []; | |
var nextTickQueue = []; | |
var deferQueue = []; | |
var lastTime = Date.now(); | |
var frameTime; | |
var frameTimeLimit; | |
var loopEnabled = true; | |
var eventForwarders = {}; | |
var eventHandler = new EventHandler(); | |
var options = { | |
containerType: 'div', | |
containerClass: 'famous-container', | |
fpsCap: undefined, | |
runLoop: true | |
}; | |
var optionsManager = new OptionsManager(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() { | |
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 | |
for (i = 0; i < nextTickQueue.length; i++) nextTickQueue[i].call(this); | |
nextTickQueue.splice(0); | |
// 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(); | |
requestAnimationFrame(loop); | |
} | |
else loopEnabled = false; | |
} | |
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(); | |
// prevent scrolling via browser | |
window.addEventListener('touchmove', function(event) { | |
event.preventDefault(); | |
}, true); | |
/** | |
* 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); | |
if (document.body) { | |
document.body.addEventListener(type, eventForwarders[type]); | |
} | |
else { | |
Engine.nextTick(function(type, forwarder) { | |
document.body.addEventListener(type, forwarder); | |
}.bind(this, type, eventForwarders[type])); | |
} | |
} | |
return eventHandler.on(type, 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 | |
*/ | |
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() { | |
return optionsManager.getOptions.apply(optionsManager, arguments); | |
}; | |
/** | |
* 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) { | |
var needMountContainer = false; | |
if (!el) { | |
el = document.createElement(options.containerType); | |
el.classList.add(options.containerClass); | |
needMountContainer = true; | |
} | |
var context = new Context(el); | |
Engine.registerContext(context); | |
if (needMountContainer) { | |
Engine.nextTick(function(context, el) { | |
document.body.appendChild(el); | |
context.emit('resize'); | |
}.bind(this, context, el)); | |
} | |
return context; | |
}; | |
/** | |
* 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; | |
}; | |
/** | |
* 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; | |
requestAnimationFrame(loop); | |
} | |
} | |
}); | |
module.exports = Engine; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Surface',['require','exports','module','./Entity','./EventHandler','./Transform'],function(require, exports, module) { | |
var Entity = require('./Entity'); | |
var EventHandler = require('./EventHandler'); | |
var Transform = require('./Transform'); | |
var devicePixelRatio = window.devicePixelRatio || 1; | |
var usePrefix = document.createElement('div').style.webkitTransform !== undefined; | |
/** | |
* 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 inner content | |
* @param {Array} [options.properties] string dictionary of HTML attributes to set on target div | |
* @param {string} [options.content] inner (HTML) content of surface | |
*/ | |
function Surface(options) { | |
this.options = {}; | |
this.properties = {}; | |
this.content = ''; | |
this.classList = []; | |
this.size = null; | |
this._classesDirty = true; | |
this._stylesDirty = true; | |
this._sizeDirty = true; | |
this._contentDirty = true; | |
this._dirtyClasses = []; | |
this._matrix = null; | |
this._opacity = 1; | |
this._origin = null; | |
this._size = null; | |
/** @ignore */ | |
this.eventForwarder = function eventForwarder(event) { | |
this.emit(event.type, event); | |
}.bind(this); | |
this.eventHandler = new EventHandler(); | |
this.eventHandler.bindThis(this); | |
this.id = Entity.register(this); | |
if (options) this.setOptions(options); | |
this._currTarget = null; | |
} | |
Surface.prototype.elementType = 'div'; | |
Surface.prototype.elementClass = 'famous-surface'; | |
/** | |
* 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 | |
*/ | |
Surface.prototype.on = function on(type, fn) { | |
if (this._currTarget) this._currTarget.addEventListener(type, this.eventForwarder); | |
this.eventHandler.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 | |
*/ | |
Surface.prototype.removeListener = function removeListener(type, fn) { | |
this.eventHandler.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 | |
*/ | |
Surface.prototype.emit = function emit(type, event) { | |
if (event && !event.origin) event.origin = this; | |
var handled = this.eventHandler.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 | |
*/ | |
Surface.prototype.pipe = function pipe(target) { | |
return this.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 | |
*/ | |
Surface.prototype.unpipe = function unpipe(target) { | |
return this.eventHandler.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) | |
*/ | |
Surface.prototype.render = function render() { | |
return this.id; | |
}; | |
/** | |
* 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 | |
* @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; | |
}; | |
/** | |
* 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 | |
* @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; | |
} | |
}; | |
/** | |
* 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 | |
* @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; | |
} | |
}; | |
/** | |
* Reset class list to provided dictionary. | |
* @method setClasses | |
* @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]); | |
}; | |
/** | |
* 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 | |
* @param {string|Document Fragment} content HTML content | |
*/ | |
Surface.prototype.setContent = function setContent(content) { | |
if (this.content !== content) { | |
this.content = content; | |
this._contentDirty = true; | |
} | |
}; | |
/** | |
* 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 | |
* @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.content) this.setContent(options.content); | |
}; | |
// Attach Famous event handling to document events emanating from target | |
// document element. This occurs just after deployment to the document. | |
// Calling this enables methods like #on and #pipe. | |
function _addEventListeners(target) { | |
for (var i in this.eventHandler.listeners) { | |
target.addEventListener(i, this.eventForwarder); | |
} | |
} | |
// Detach Famous event handling from document events emanating from target | |
// document element. This occurs just before recall from the document. | |
function _removeEventListeners(target) { | |
for (var i in this.eventHandler.listeners) { | |
target.removeEventListener(i, this.eventForwarder); | |
} | |
} | |
// 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] = ''; | |
} | |
} | |
/** | |
* 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 (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { | |
_setMatrix = function(element, matrix) { | |
element.style.zIndex = (matrix[14] * 1000000) | 0; // fix for Firefox z-buffer issues | |
element.style.transform = _formatCSSTransform(matrix); | |
}; | |
} | |
else if (usePrefix) { | |
_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 ? 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 ? function(element) { | |
element.style.webkitTransform = 'scale3d(0.0001,0.0001,1)'; | |
element.style.opacity = 0; | |
} : function(element) { | |
element.style.transform = 'scale3d(0.0001,0.0001,1)'; | |
element.style.opacity = 0; | |
}; | |
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 = ''; | |
_addEventListeners.call(this, target); | |
this._currTarget = target; | |
this._stylesDirty = true; | |
this._classesDirty = true; | |
this._sizeDirty = true; | |
this._contentDirty = true; | |
this._matrix = null; | |
this._opacity = undefined; | |
this._origin = null; | |
this._size = null; | |
}; | |
/** | |
* 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._currTarget) this.setup(context.allocator); | |
var target = this._currTarget; | |
var matrix = context.transform; | |
var opacity = context.opacity; | |
var origin = context.origin; | |
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; | |
} | |
if (this._stylesDirty) { | |
_applyStyles.call(this, target); | |
this._stylesDirty = false; | |
} | |
if (this._contentDirty) { | |
this.deploy(target); | |
this.eventHandler.emit('deploy'); | |
this._contentDirty = false; | |
} | |
if (this.size) { | |
var origSize = size; | |
size = [this.size[0], this.size[1]]; | |
if (size[0] === undefined && origSize[0]) size[0] = origSize[0]; | |
if (size[1] === undefined && origSize[1]) size[1] = origSize[1]; | |
} | |
if (size[0] === true) size[0] = target.clientWidth; | |
if (size[1] === true) size[1] = target.clientHeight; | |
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 (!matrix && this._matrix) { | |
this._matrix = null; | |
this._opacity = 0; | |
_setInvisible(target); | |
return; | |
} | |
if (this._opacity !== opacity) { | |
this._opacity = opacity; | |
target.style.opacity = (opacity >= 1) ? '0.999999' : opacity; | |
} | |
if (_xyNotEquals(this._origin, origin) || Transform.notEquals(this._matrix, matrix) || this._sizeDirty) { | |
if (!matrix) matrix = Transform.identity; | |
this._matrix = matrix; | |
var aaMatrix = matrix; | |
if (origin) { | |
if (!this._origin) this._origin = [0, 0]; | |
this._origin[0] = origin[0]; | |
this._origin[1] = origin[1]; | |
aaMatrix = Transform.thenMove(matrix, [-this._size[0] * origin[0], -this._size[1] * origin[1], 0]); | |
_setOrigin(target, origin); | |
} | |
_setMatrix(target, aaMatrix); | |
} | |
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._sizeDirty = false; | |
} | |
}; | |
/** | |
* 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._currTarget; | |
this.eventHandler.emit('recall'); | |
this.recall(target); | |
target.style.display = 'none'; | |
target.style.width = ''; | |
target.style.height = ''; | |
this._size = null; | |
_cleanupStyles.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); | |
} | |
} | |
_removeEventListeners.call(this, target); | |
this._currTarget = null; | |
allocator.deallocate(target); | |
_setInvisible(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 | |
* @param {boolean} actual return computed size rather than provided | |
* @return {Array.Number} [x,y] size of surface | |
*/ | |
Surface.prototype.getSize = function getSize(actual) { | |
return actual ? this._size : (this.size || this._size); | |
}; | |
/** | |
* Set x and y dimensions of the surface. | |
* | |
* @method setSize | |
* @param {Array.Number} size as [width, height] | |
*/ | |
Surface.prototype.setSize = function setSize(size) { | |
this.size = size ? [size[0], size[1]] : null; | |
this._sizeDirty = true; | |
}; | |
module.exports = Surface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Group',['require','exports','module','./Context','./Transform','./Surface'],function(require, exports, module) { | |
var Context = require('./Context'); | |
var Transform = require('./Transform'); | |
var Surface = require('./Surface'); | |
/** | |
* A Context designed to contain surfaces and set properties | |
* to be applied to all of them at once. | |
* This is primarily used for specific performance improvements in the rendering engine. | |
* Private. | |
* | |
* @private | |
* @class Group | |
* @extends Surface | |
* @constructor | |
* @param {Object} [options] Surface options array (see Surface}) | |
*/ | |
function Group(options) { | |
Surface.call(this, options); | |
this._shouldRecalculateSize = false; | |
this._container = document.createDocumentFragment(); | |
this.context = new Context(this._container); | |
this.setContent(this._container); | |
this._groupSize = [undefined, undefined]; | |
} | |
/** @const */ | |
Group.SIZE_ZERO = [0, 0]; | |
Group.prototype = Object.create(Surface.prototype); | |
Group.prototype.elementType = 'div'; | |
Group.prototype.elementClass = 'famous-group'; | |
/** | |
* Add renderables to this component's render tree. | |
* | |
* @method add | |
* @private | |
* @param {Object} obj renderable object | |
* @return {RenderNode} Render wrapping provided object, if not already a RenderNode | |
*/ | |
Group.prototype.add = function add() { | |
return this.context.add.apply(this.context, arguments); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {Number} Render spec for this component | |
*/ | |
Group.prototype.render = function render() { | |
return Surface.prototype.render.call(this); | |
}; | |
/** | |
* Place the document element this component manages into the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
Group.prototype.deploy = function deploy(target) { | |
this.context.migrate(target); | |
}; | |
/** | |
* Remove this component and contained content from the document | |
* | |
* @private | |
* @method recall | |
* | |
* @param {Node} target node to which the component was deployed | |
*/ | |
Group.prototype.recall = function recall(target) { | |
this._container = document.createDocumentFragment(); | |
this.context.migrate(this._container); | |
}; | |
/** | |
* Apply changes from this component to the corresponding document element. | |
* | |
* @private | |
* @method commit | |
* | |
* @param {Object} context update spec passed in from above in the render tree. | |
*/ | |
Group.prototype.commit = function commit(context) { | |
var transform = context.transform; | |
var origin = context.origin; | |
var opacity = context.opacity; | |
var size = context.size; | |
var result = Surface.prototype.commit.call(this, { | |
allocator: context.allocator, | |
transform: Transform.thenMove(transform, [-origin[0] * size[0], -origin[1] * size[1], 0]), | |
opacity: opacity, | |
origin: origin, | |
size: Group.SIZE_ZERO | |
}); | |
if (size[0] !== this._groupSize[0] || size[1] !== this._groupSize[1]) { | |
this._groupSize[0] = size[0]; | |
this._groupSize[1] = size[1]; | |
this.context.setSize(size); | |
} | |
this.context.update({ | |
transform: Transform.translate(-origin[0] * size[0], -origin[1] * size[1], 0), | |
origin: origin, | |
size: size | |
}); | |
return result; | |
}; | |
module.exports = Group; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/TransitionableTransform',['require','exports','module','./Transitionable','famous/core/Transform','famous/utilities/Utility'],function(require, exports, module) { | |
var Transitionable = require('./Transitionable'); | |
var Transform = require('famous/core/Transform'); | |
var Utility = require('famous/utilities/Utility'); | |
/** | |
* A class for transitioning the state of a Transform by transitioning | |
* its 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.identity.slice(); | |
this.translate = new Transitionable([0, 0, 0]); | |
this.rotate = new Transitionable([0, 0, 0]); | |
this.skew = new Transitionable([0, 0, 0]); | |
this.scale = new Transitionable([1, 1, 1]); | |
if (transform) this.set(transform); | |
} | |
function _build() { | |
return Transform.build({ | |
translate: this.translate.get(), | |
rotate: this.rotate.get(), | |
skew: this.skew.get(), | |
scale: this.scale.get() | |
}); | |
} | |
/** | |
* An optimized way of setting only the translation component of a Transform | |
* | |
* @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) { | |
this.translate.set(translate, transition, callback); | |
this._final = this._final.slice(); | |
this._final[12] = translate[0]; | |
this._final[13] = translate[1]; | |
if (translate[2] !== undefined) this._final[14] = translate[2]; | |
return this; | |
}; | |
/** | |
* An optimized way of setting only the scale component of a Transform | |
* | |
* @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) { | |
this.scale.set(scale, transition, callback); | |
this._final = this._final.slice(); | |
this._final[0] = scale[0]; | |
this._final[5] = scale[1]; | |
if (scale[2] !== undefined) this._final[10] = scale[2]; | |
return this; | |
}; | |
/** | |
* An optimized way of setting only the rotational component of a Transform | |
* | |
* @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) { | |
this.rotate.set(eulerAngles, transition, callback); | |
this._final = _build.call(this); | |
this._final = Transform.build({ | |
translate: this.translate.get(), | |
rotate: eulerAngles, | |
scale: this.scale.get(), | |
skew: this.skew.get() | |
}); | |
return this; | |
}; | |
/** | |
* An optimized way of setting only the skew component of a Transform | |
* | |
* @method setSkew | |
* @chainable | |
* | |
* @param skewAngles {Array} New skew state | |
* @param [transition] {Object} Transition definition | |
* @param [callback] {Function} Callback | |
* @return {TransitionableTransform} | |
*/ | |
TransitionableTransform.prototype.setSkew = function setSkew(skewAngles, transition, callback) { | |
this.skew.set(skewAngles, transition, callback); | |
this._final = Transform.build({ | |
translate: this.translate.get(), | |
rotate: this.rotate.get(), | |
scale: this.scale.get(), | |
skew: skewAngles | |
}); | |
return this; | |
}; | |
/** | |
* Setter for a TransitionableTransform with optional parameters to transition | |
* between Transforms | |
* | |
* @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) { | |
this._final = transform; | |
var components = Transform.interpret(transform); | |
var _callback = callback ? Utility.after(4, callback) : null; | |
this.translate.set(components.translate, transition, _callback); | |
this.rotate.set(components.rotate, transition, _callback); | |
this.skew.set(components.skew, transition, _callback); | |
this.scale.set(components.scale, 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) { | |
this.translate.setDefault(transition); | |
this.rotate.setDefault(transition); | |
this.skew.setDefault(transition); | |
this.scale.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 TransitionalTransform is currently transitioning | |
* | |
* @method isActive | |
* | |
* @return {Boolean} | |
*/ | |
TransitionableTransform.prototype.isActive = function isActive() { | |
return this.translate.isActive() || this.rotate.isActive() || this.scale.isActive() || this.skew.isActive(); | |
}; | |
/** | |
* Halts the transition | |
* | |
* @method halt | |
*/ | |
TransitionableTransform.prototype.halt = function halt() { | |
this._final = this.get(); | |
this.translate.halt(); | |
this.rotate.halt(); | |
this.skew.halt(); | |
this.scale.halt(); | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Modifier',['require','exports','module','./Transform','famous/transitions/Transitionable','famous/transitions/TransitionableTransform'],function(require, exports, module) { | |
var Transform = require('./Transform'); | |
/* TODO: remove these dependencies when deprecation complete */ | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var TransitionableTransform = require('famous/transitions/TransitionableTransform'); | |
/** | |
* | |
* 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; | |
/* TODO: remove this when deprecation complete */ | |
this._legacyStates = {}; | |
this._output = { | |
transform: Transform.identity, | |
opacity: 1, | |
origin: null, | |
align: null, | |
size: 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); | |
} | |
} | |
/** | |
* 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; | |
}; | |
/** | |
* 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(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(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(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(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(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 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(); | |
this._transformGetter = null; | |
this._opacityGetter = null; | |
this._originGetter = null; | |
this._alignGetter = null; | |
this._sizeGetter = 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; | |
}; | |
// 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(); | |
} | |
/** | |
* 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; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/Scene',['require','exports','module','./Transform','./Modifier','./RenderNode'],function(require, exports, module) { | |
var Transform = require('./Transform'); | |
var Modifier = require('./Modifier'); | |
var RenderNode = require('./RenderNode'); | |
/** | |
* Builds and renders a scene graph based on a declarative structure definition. | |
* See the Scene examples in the examples distribution (http://github.com/Famous/examples.git). | |
* | |
* @class Scene | |
* @constructor | |
* @param {Object} definition in the format of a render spec. | |
*/ | |
function Scene(definition) { | |
this.id = null; | |
this._objects = null; | |
this.node = new RenderNode(); | |
this._definition = null; | |
if (definition) this.load(definition); | |
} | |
var _MATRIX_GENERATORS = { | |
'translate': Transform.translate, | |
'rotate': Transform.rotate, | |
'rotateX': Transform.rotateX, | |
'rotateY': Transform.rotateY, | |
'rotateZ': Transform.rotateZ, | |
'rotateAxis': Transform.rotateAxis, | |
'scale': Transform.scale, | |
'skew': Transform.skew, | |
'matrix3d': function() { | |
return arguments; | |
} | |
}; | |
/** | |
* Clone this scene | |
* | |
* @method create | |
* @return {Scene} deep copy of this scene | |
*/ | |
Scene.prototype.create = function create() { | |
return new Scene(this._definition); | |
}; | |
function _resolveTransformMatrix(matrixDefinition) { | |
for (var type in _MATRIX_GENERATORS) { | |
if (type in matrixDefinition) { | |
var args = matrixDefinition[type]; | |
if (!(args instanceof Array)) args = [args]; | |
return _MATRIX_GENERATORS[type].apply(this, args); | |
} | |
} | |
} | |
// parse transform into tree of render nodes, doing matrix multiplication | |
// when available | |
function _parseTransform(definition) { | |
var transformDefinition = definition.transform; | |
var opacity = definition.opacity; | |
var origin = definition.origin; | |
var size = definition.size; | |
var transform = Transform.identity; | |
if (transformDefinition instanceof Array) { | |
if (transformDefinition.length === 16 && typeof transformDefinition[0] === 'number') { | |
transform = transformDefinition; | |
} | |
else { | |
for (var i = 0; i < transformDefinition.length; i++) { | |
transform = Transform.multiply(transform, _resolveTransformMatrix(transformDefinition[i])); | |
} | |
} | |
} | |
else if (transformDefinition instanceof Object) { | |
transform = _resolveTransformMatrix(transformDefinition); | |
} | |
var result = new Modifier({ | |
transform: transform, | |
opacity: opacity, | |
origin: origin, | |
size: size | |
}); | |
return result; | |
} | |
function _parseArray(definition) { | |
var result = new RenderNode(); | |
for (var i = 0; i < definition.length; i++) { | |
var obj = _parse.call(this, definition[i]); | |
if (obj) result.add(obj); | |
} | |
return result; | |
} | |
// parse object directly into tree of RenderNodes | |
function _parse(definition) { | |
var result; | |
var id; | |
if (definition instanceof Array) { | |
result = _parseArray.call(this, definition); | |
} | |
else { | |
id = this._objects.length; | |
if (definition.render && (definition.render instanceof Function)) { | |
result = definition; | |
} | |
else if (definition.target) { | |
var targetObj = _parse.call(this, definition.target); | |
var obj = _parseTransform.call(this, definition); | |
result = new RenderNode(obj); | |
result.add(targetObj); | |
if (definition.id) this.id[definition.id] = obj; | |
} | |
else if (definition.id) { | |
result = new RenderNode(); | |
this.id[definition.id] = result; | |
} | |
} | |
this._objects[id] = result; | |
return result; | |
} | |
/** | |
* Builds and renders a scene graph based on a canonical declarative scene definition. | |
* See examples/Scene/example.js. | |
* | |
* @method load | |
* @param {Object} definition definition in the format of a render spec. | |
*/ | |
Scene.prototype.load = function load(definition) { | |
this._definition = definition; | |
this.id = {}; | |
this._objects = []; | |
this.node.set(_parse.call(this, definition)); | |
}; | |
/** | |
* Add renderables to this component's render tree | |
* | |
* @method add | |
* | |
* @param {Object} obj renderable object | |
* @return {RenderNode} Render wrapping provided object, if not already a RenderNode | |
*/ | |
Scene.prototype.add = function add() { | |
return this.node.add.apply(this.node, arguments); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
Scene.prototype.render = function render() { | |
return this.node.render.apply(this.node, arguments); | |
}; | |
module.exports = Scene; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/View',['require','exports','module','./EventHandler','./OptionsManager','./RenderNode'],function(require, exports, module) { | |
var EventHandler = require('./EventHandler'); | |
var OptionsManager = require('./OptionsManager'); | |
var RenderNode = require('./RenderNode'); | |
/** | |
* Useful for quickly creating elements within applications | |
* with large event systems. Consists of a RenderNode paired with | |
* an input EventHandler and an output EventHandler. | |
* Meant to be extended by the developer. | |
* | |
* @class View | |
* @uses EventHandler | |
* @uses OptionsManager | |
* @uses RenderNode | |
* @constructor | |
*/ | |
function View(options) { | |
this._node = new RenderNode(); | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS || View.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
} | |
View.DEFAULT_OPTIONS = {}; // no defaults | |
/** | |
* Look up options value by key | |
* @method getOptions | |
* | |
* @param {string} key key | |
* @return {Object} associated object | |
*/ | |
View.prototype.getOptions = function getOptions() { | |
return this._optionsManager.value(); | |
}; | |
/* | |
* Set internal options. | |
* No defaults options are set in View. | |
* | |
* @method setOptions | |
* @param {Object} options | |
*/ | |
View.prototype.setOptions = function setOptions(options) { | |
this._optionsManager.patch(options); | |
}; | |
/** | |
* Add a child renderable to the view. | |
* Note: This is meant to be used by an inheriting class | |
* rather than from outside the prototype chain. | |
* | |
* @method add | |
* @return {RenderNode} | |
* @protected | |
*/ | |
View.prototype.add = function add() { | |
return this._node.add.apply(this._node, arguments); | |
}; | |
/** | |
* Alias for add | |
* @method _add | |
*/ | |
View.prototype._add = View.prototype.add; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
View.prototype.render = function render() { | |
return this._node.render(); | |
}; | |
/** | |
* Return size of contained element. | |
* | |
* @method getSize | |
* @return {Array.Number} [width, height] | |
*/ | |
View.prototype.getSize = function getSize() { | |
if (this._node && this._node.getSize) { | |
return this._node.getSize.apply(this._node, arguments) || this.options.size; | |
} | |
else return this.options.size; | |
}; | |
module.exports = View; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/core/ViewSequence',['require','exports','module'],function(require, exports, module) { | |
/** | |
* Helper object used to iterate through items sequentially. Used in | |
* views that deal with layout. A ViewSequence object conceptually points | |
* to a node in a linked list. | |
* | |
* @class ViewSequence | |
* | |
* @constructor | |
* @param {Object|Array} options Options object, or content array. | |
* @param {Number} [options.index] starting index. | |
* @param {Number} [options.array] Array of elements to populate the ViewSequence | |
* @param {Object} [options._] Optional backing store (internal | |
* @param {Boolean} [options.loop] Whether to wrap when accessing elements just past the end | |
* (or beginning) of the sequence. | |
*/ | |
function ViewSequence(options) { | |
if (!options) options = []; | |
if (options instanceof Array) options = {array: options}; | |
this._ = null; | |
this.index = options.index || 0; | |
if (options.array) this._ = new (this.constructor.Backing)(options.array); | |
else if (options._) this._ = options._; | |
if (this.index === this._.firstIndex) this._.firstNode = this; | |
if (this.index === this._.firstIndex + this._.array.length - 1) this._.lastNode = this; | |
if (options.loop !== undefined) this._.loop = options.loop; | |
this._previousNode = null; | |
this._nextNode = null; | |
} | |
// constructor for internal storage | |
ViewSequence.Backing = function Backing(array) { | |
this.array = array; | |
this.firstIndex = 0; | |
this.loop = false; | |
this.firstNode = null; | |
this.lastNode = null; | |
}; | |
// Get value "i" slots away from the first index. | |
ViewSequence.Backing.prototype.getValue = function getValue(i) { | |
var _i = i - this.firstIndex; | |
if (_i < 0 || _i >= this.array.length) return null; | |
return this.array[_i]; | |
}; | |
// Set value "i" slots away from the first index. | |
ViewSequence.Backing.prototype.setValue = function setValue(i, value) { | |
this.array[i - this.firstIndex] = value; | |
}; | |
// After splicing into the backing store, restore the indexes of each node correctly. | |
ViewSequence.Backing.prototype.reindex = function reindex(start, removeCount, insertCount) { | |
if (!this.array[0]) return; | |
var i = 0; | |
var index = this.firstIndex; | |
var indexShiftAmount = insertCount - removeCount; | |
var node = this.firstNode; | |
// find node to begin | |
while (index < start - 1) { | |
node = node.getNext(); | |
index++; | |
} | |
// skip removed nodes | |
var spliceStartNode = node; | |
for (i = 0; i < removeCount; i++) { | |
node = node.getNext(); | |
if (node) node._previousNode = spliceStartNode; | |
} | |
var spliceResumeNode = node ? node.getNext() : null; | |
// generate nodes for inserted items | |
spliceStartNode._nextNode = null; | |
node = spliceStartNode; | |
for (i = 0; i < insertCount; i++) node = node.getNext(); | |
index += insertCount; | |
// resume the chain | |
if (node !== spliceResumeNode) { | |
node._nextNode = spliceResumeNode; | |
if (spliceResumeNode) spliceResumeNode._previousNode = node; | |
} | |
if (spliceResumeNode) { | |
node = spliceResumeNode; | |
index++; | |
while (node && index < this.array.length + this.firstIndex) { | |
if (node._nextNode) node.index += indexShiftAmount; | |
else node.index = index; | |
node = node.getNext(); | |
index++; | |
} | |
} | |
}; | |
/** | |
* Return ViewSequence node previous to this node in the list, respecting looping if applied. | |
* | |
* @method getPrevious | |
* @return {ViewSequence} previous node. | |
*/ | |
ViewSequence.prototype.getPrevious = function getPrevious() { | |
if (this.index === this._.firstIndex) { | |
if (this._.loop) { | |
this._previousNode = this._.lastNode || new (this.constructor)({_: this._, index: this._.firstIndex + this._.array.length - 1}); | |
this._previousNode._nextNode = this; | |
} | |
else { | |
this._previousNode = null; | |
} | |
} | |
else if (!this._previousNode) { | |
this._previousNode = new (this.constructor)({_: this._, index: this.index - 1}); | |
this._previousNode._nextNode = this; | |
} | |
return this._previousNode; | |
}; | |
/** | |
* Return ViewSequence node next after this node in the list, respecting looping if applied. | |
* | |
* @method getNext | |
* @return {ViewSequence} previous node. | |
*/ | |
ViewSequence.prototype.getNext = function getNext() { | |
if (this.index === this._.firstIndex + this._.array.length - 1) { | |
if (this._.loop) { | |
this._nextNode = this._.firstNode || new (this.constructor)({_: this._, index: this._.firstIndex}); | |
this._nextNode._previousNode = this; | |
} | |
else { | |
this._nextNode = null; | |
} | |
} | |
else if (!this._nextNode) { | |
this._nextNode = new (this.constructor)({_: this._, index: this.index + 1}); | |
this._nextNode._previousNode = this; | |
} | |
return this._nextNode; | |
}; | |
/** | |
* Return index of this ViewSequence node. | |
* | |
* @method getIndex | |
* @return {Number} index | |
*/ | |
ViewSequence.prototype.getIndex = function getIndex() { | |
return this.index; | |
}; | |
/** | |
* Return printable version of this ViewSequence node. | |
* | |
* @method toString | |
* @return {string} this index as a string | |
*/ | |
ViewSequence.prototype.toString = function toString() { | |
return '' + this.index; | |
}; | |
/** | |
* Add one or more objects to the beginning of the sequence. | |
* | |
* @method unshift | |
* @param {...Object} value arguments array of objects | |
*/ | |
ViewSequence.prototype.unshift = function unshift(value) { | |
this._.array.unshift.apply(this._.array, arguments); | |
this._.firstIndex -= arguments.length; | |
}; | |
/** | |
* Add one or more objects to the end of the sequence. | |
* | |
* @method push | |
* @param {...Object} value arguments array of objects | |
*/ | |
ViewSequence.prototype.push = function push(value) { | |
this._.array.push.apply(this._.array, arguments); | |
}; | |
/** | |
* Remove objects from the sequence | |
* | |
* @method splice | |
* @param {Number} index starting index for removal | |
* @param {Number} howMany how many elements to remove | |
* @param {...Object} value arguments array of objects | |
*/ | |
ViewSequence.prototype.splice = function splice(index, howMany) { | |
var values = Array.prototype.slice.call(arguments, 2); | |
this._.array.splice.apply(this._.array, [index - this._.firstIndex, howMany].concat(values)); | |
this._.reindex(index, howMany, values.length); | |
}; | |
/** | |
* Exchange this element's sequence position with another's. | |
* | |
* @method swap | |
* @param {ViewSequence} other element to swap with. | |
*/ | |
ViewSequence.prototype.swap = function swap(other) { | |
var otherValue = other.get(); | |
var myValue = this.get(); | |
this._.setValue(this.index, otherValue); | |
this._.setValue(other.index, myValue); | |
var myPrevious = this._previousNode; | |
var myNext = this._nextNode; | |
var myIndex = this.index; | |
var otherPrevious = other._previousNode; | |
var otherNext = other._nextNode; | |
var otherIndex = other.index; | |
this.index = otherIndex; | |
this._previousNode = (otherPrevious === this) ? other : otherPrevious; | |
if (this._previousNode) this._previousNode._nextNode = this; | |
this._nextNode = (otherNext === this) ? other : otherNext; | |
if (this._nextNode) this._nextNode._previousNode = this; | |
other.index = myIndex; | |
other._previousNode = (myPrevious === other) ? this : myPrevious; | |
if (other._previousNode) other._previousNode._nextNode = other; | |
other._nextNode = (myNext === other) ? this : myNext; | |
if (other._nextNode) other._nextNode._previousNode = other; | |
if (this.index === this._.firstIndex) this._.firstNode = this; | |
else if (this.index === this._.firstIndex + this._.array.length - 1) this._.lastNode = this; | |
if (other.index === this._.firstIndex) this._.firstNode = other; | |
else if (other.index === this._.firstIndex + this._.array.length - 1) this._.lastNode = other; | |
}; | |
/** | |
* Return value of this ViewSequence node. | |
* | |
* @method get | |
* @return {Object} value of thiss | |
*/ | |
ViewSequence.prototype.get = function get() { | |
return this._.getValue(this.index); | |
}; | |
/** | |
* Call getSize() on the contained View. | |
* | |
* @method getSize | |
* @return {Array.Number} [width, height] | |
*/ | |
ViewSequence.prototype.getSize = function getSize() { | |
var target = this.get(); | |
return target ? target.getSize() : null; | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* Specifically, this will render the value at the current index. | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
ViewSequence.prototype.render = function render() { | |
var target = this.get(); | |
return target ? target.render.apply(target, arguments) : null; | |
}; | |
module.exports = ViewSequence; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/math/Utilities',['require','exports','module'],function(require, exports, module) { | |
/** | |
* A few static methods. | |
* | |
* @class Utilities | |
* @static | |
*/ | |
var Utilities = {}; | |
/** | |
* Constrain input to range. | |
* | |
* @method clamp | |
* @param {Number} value input | |
* @param {Array.Number} range [min, max] | |
* @static | |
*/ | |
Utilities.clamp = function clamp(value, range) { | |
return Math.max(Math.min(value, range[1]), range[0]); | |
}; | |
/** | |
* Euclidean length of numerical array. | |
* | |
* @method length | |
* @param {Array.Number} array array of numbers | |
* @static | |
*/ | |
Utilities.length = function length(array) { | |
var distanceSquared = 0; | |
for (var i = 0; i < array.length; i++) { | |
distanceSquared += array[i] * array[i]; | |
} | |
return Math.sqrt(distanceSquared); | |
}; | |
module.exports = Utilities; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/GenericSync',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* 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(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.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]){ | |
if (registry[key] === syncObject[key]) return; // redundant registration | |
else throw new Error('this key is registered to a different sync class'); | |
} | |
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]); | |
}; | |
module.exports = GenericSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/MouseSync',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Handles piped in mouse drag events. Outputs an object with two | |
* properties, position and velocity. | |
* Emits 'start', 'update' and 'end' events with DOM event passthroughs, | |
* with position, velocity, and a delta key. | |
* | |
* @class MouseSync | |
* @constructor | |
* | |
* @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.propogate] {Boolean} add listened to document on mouseleave | |
*/ | |
function MouseSync(options) { | |
this.options = Object.create(MouseSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this._eventInput.on('mousedown', _handleStart.bind(this)); | |
this._eventInput.on('mousemove', _handleMove.bind(this)); | |
this._eventInput.on('mouseup', _handleEnd.bind(this)); | |
if (this.options.propogate) this._eventInput.on('mouseleave', _handleLeave.bind(this)); | |
else this._eventInput.on('mouseleave', _handleEnd.bind(this)); | |
this._payload = { | |
delta : null, | |
position : null, | |
velocity : null, | |
clientX : 0, | |
clientY : 0, | |
offsetX : 0, | |
offsetY : 0 | |
}; | |
this._position = null; // to be deprecated | |
this._prevCoord = undefined; | |
this._prevTime = undefined; | |
this._down = false; | |
this._moved = false; | |
} | |
MouseSync.DEFAULT_OPTIONS = { | |
direction: undefined, | |
rails: false, | |
scale: 1, | |
propogate: true // events piped to document on mouseleave | |
}; | |
MouseSync.DIRECTION_X = 0; | |
MouseSync.DIRECTION_Y = 1; | |
var MINIMUM_TICK_TIME = 8; | |
var _now = Date.now; | |
function _handleStart(event) { | |
var delta; | |
var velocity; | |
event.preventDefault(); // prevent drag | |
var x = event.clientX; | |
var y = event.clientY; | |
this._prevCoord = [x, y]; | |
this._prevTime = _now(); | |
this._down = true; | |
this._move = false; | |
if (this.options.direction !== undefined){ | |
this._position = 0; | |
delta = 0; | |
velocity = 0; | |
} | |
else { | |
this._position = [0, 0]; | |
delta = [0, 0]; | |
velocity = [0, 0]; | |
} | |
var payload = this._payload; | |
payload.delta = delta; | |
payload.position = this._position; | |
payload.velocity = velocity; | |
payload.clientX = x; | |
payload.clientY = y; | |
payload.offsetX = event.offsetX; | |
payload.offsetY = event.offsetY; | |
this._eventOutput.emit('start', payload); | |
} | |
function _handleMove(event) { | |
if (!this._prevCoord) return; | |
var prevCoord = this._prevCoord; | |
var prevTime = this._prevTime; | |
var x = event.clientX; | |
var y = event.clientY; | |
var currTime = _now(); | |
var diffX = x - prevCoord[0]; | |
var diffY = y - prevCoord[1]; | |
if (this.options.rails) { | |
if (Math.abs(diffX) > Math.abs(diffY)) diffY = 0; | |
else diffX = 0; | |
} | |
var diffTime = Math.max(currTime - prevTime, MINIMUM_TICK_TIME); // minimum tick time | |
var velX = diffX / diffTime; | |
var velY = diffY / diffTime; | |
var scale = this.options.scale; | |
var nextVel; | |
var nextDelta; | |
if (this.options.direction === MouseSync.DIRECTION_X) { | |
nextDelta = scale * diffX; | |
nextVel = scale * velX; | |
this._position += nextDelta; | |
} | |
else if (this.options.direction === MouseSync.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.position = this._position; | |
payload.velocity = nextVel; | |
payload.clientX = x; | |
payload.clientY = y; | |
payload.offsetX = event.offsetX; | |
payload.offsetY = event.offsetY; | |
this._eventOutput.emit('update', payload); | |
this._prevCoord = [x, y]; | |
this._prevTime = currTime; | |
this._move = true; | |
} | |
function _handleEnd(event) { | |
if (!this._down) return; | |
this._eventOutput.emit('end', this._payload); | |
this._prevCoord = undefined; | |
this._prevTime = undefined; | |
this._down = false; | |
this._move = false; | |
} | |
function _handleLeave(event) { | |
if (!this._down || !this._move) return; | |
var boundMove = _handleMove.bind(this); | |
var boundEnd = function(event) { | |
_handleEnd.call(this, event); | |
document.removeEventListener('mousemove', boundMove); | |
document.removeEventListener('mouseup', boundEnd); | |
}.bind(this, event); | |
document.addEventListener('mousemove', boundMove); | |
document.addEventListener('mouseup', boundEnd); | |
} | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
MouseSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
/** | |
* 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.propogate] {Boolean} add listened to document on mouseleave | |
*/ | |
MouseSync.prototype.setOptions = function setOptions(options) { | |
if (options.direction !== undefined) this.options.direction = options.direction; | |
if (options.rails !== undefined) this.options.rails = options.rails; | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
if (options.propogate !== undefined) this.options.propogate = options.propogate; | |
}; | |
module.exports = MouseSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/TouchTracker',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
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(event) { | |
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(event) { | |
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(event) { | |
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]; | |
} | |
} | |
} | |
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 {Boolean} selective if false, save state for each touch. | |
*/ | |
function TouchTracker(selective) { | |
this.selective = selective; | |
this.touchHistory = {}; | |
this.eventInput = new EventHandler(); | |
this.eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this.eventInput); | |
EventHandler.setOutputHandler(this, this.eventOutput); | |
this.eventInput.on('touchstart', _handleStart.bind(this)); | |
this.eventInput.on('touchmove', _handleMove.bind(this)); | |
this.eventInput.on('touchend', _handleEnd.bind(this)); | |
this.eventInput.on('touchcancel', _handleEnd.bind(this)); | |
this.eventInput.on('unpipe', _handleUnpipe.bind(this)); | |
} | |
/** | |
* 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]; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/TouchSync',['require','exports','module','./TouchTracker','famous/core/EventHandler'],function(require, exports, module) { | |
var TouchTracker = require('./TouchTracker'); | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Handles piped in touch events. Emits 'start', 'update', and 'events' | |
* events with position, velocity, acceleration, and touch id. | |
* Useful for dealing with inputs on touch devices. | |
* | |
* | |
* @class TouchSync | |
* @constructor | |
* | |
* @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 | |
*/ | |
function TouchSync(options) { | |
this.options = Object.create(TouchSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._eventOutput = new EventHandler(); | |
this._touchTracker = new TouchTracker(); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
EventHandler.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, | |
scale: 1 | |
}; | |
TouchSync.DIRECTION_X = 0; | |
TouchSync.DIRECTION_Y = 1; | |
var MINIMUM_TICK_TIME = 8; | |
// handle 'trackstart' | |
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); | |
} | |
// handle 'trackmove' | |
function _handleMove(data) { | |
var history = data.history; | |
var currHistory = history[history.length - 1]; | |
var prevHistory = history[history.length - 2]; | |
var prevTime = prevHistory.timestamp; | |
var currTime = currHistory.timestamp; | |
var diffX = currHistory.x - prevHistory.x; | |
var diffY = currHistory.y - prevHistory.y; | |
if (this.options.rails) { | |
if (Math.abs(diffX) > Math.abs(diffY)) diffY = 0; | |
else diffX = 0; | |
} | |
var diffTime = Math.max(currTime - prevTime, MINIMUM_TICK_TIME); | |
var velX = diffX / diffTime; | |
var velY = diffY / 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); | |
} | |
// handle 'trackend' | |
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) { | |
if (options.direction !== undefined) this.options.direction = options.direction; | |
if (options.rails !== undefined) this.options.rails = options.rails; | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
}; | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
TouchSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/modifiers/Draggable',['require','exports','module','famous/core/Transform','famous/transitions/Transitionable','famous/core/EventHandler','famous/math/Utilities','famous/inputs/GenericSync','famous/inputs/MouseSync','famous/inputs/TouchSync'],function(require, exports, module) { | |
var Transform = require('famous/core/Transform'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Utilities = require('famous/math/Utilities'); | |
var GenericSync = require('famous/inputs/GenericSync'); | |
var MouseSync = require('famous/inputs/MouseSync'); | |
var TouchSync = require('famous/inputs/TouchSync'); | |
GenericSync.register({'mouse': MouseSync, 'touch': TouchSync}); | |
/** | |
* Makes added render nodes responsive to drag beahvior. | |
* Emits events 'start', 'update', 'end'. | |
* @class Draggable | |
* @constructor | |
* @param {Object} [options] options configuration object. | |
* @param {Number} [options.snapX] grid width for snapping during drag | |
* @param {Number} [options.snapY] grid height for snapping during drag | |
* @param {Array.Number} [options.xRange] maxmimum [negative, positive] x displacement from start of drag | |
* @param {Array.Number} [options.yRange] maxmimum [negative, positive] y displacement from start of drag | |
* @param {Number} [options.scale] one pixel of input motion translates to this many pixels of output drag motion | |
* @param {Number} [options.projection] User should set to Draggable._direction.x or | |
* Draggable._direction.y to constrain to one axis. | |
* | |
*/ | |
function Draggable(options) { | |
this.options = Object.create(Draggable.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._positionState = new Transitionable([0,0]); | |
this._differential = [0,0]; | |
this._active = true; | |
this.sync = new GenericSync(['mouse', 'touch'], {scale : this.options.scale}); | |
this.eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this.sync); | |
EventHandler.setOutputHandler(this, this.eventOutput); | |
_bindEvents.call(this); | |
} | |
//binary representation of directions for bitwise operations | |
var _direction = { | |
x : 0x01, //001 | |
y : 0x02 //010 | |
}; | |
Draggable.DIRECTION_X = _direction.x; | |
Draggable.DIRECTION_Y = _direction.y; | |
var _clamp = Utilities.clamp; | |
Draggable.DEFAULT_OPTIONS = { | |
projection : _direction.x | _direction.y, | |
scale : 1, | |
xRange : null, | |
yRange : null, | |
snapX : 0, | |
snapY : 0, | |
transition : {duration : 0} | |
}; | |
function _mapDifferential(differential) { | |
var opts = this.options; | |
var projection = opts.projection; | |
var snapX = opts.snapX; | |
var snapY = opts.snapY; | |
//axes | |
var tx = (projection & _direction.x) ? differential[0] : 0; | |
var ty = (projection & _direction.y) ? differential[1] : 0; | |
//snapping | |
if (snapX > 0) tx -= tx % snapX; | |
if (snapY > 0) ty -= ty % snapY; | |
return [tx, ty]; | |
} | |
function _handleStart() { | |
if (!this._active) return; | |
if (this._positionState.isActive()) this._positionState.halt(); | |
this.eventOutput.emit('start', {position : this.getPosition()}); | |
} | |
function _handleMove(event) { | |
if (!this._active) return; | |
var options = this.options; | |
this._differential = event.position; | |
var newDifferential = _mapDifferential.call(this, this._differential); | |
//buffer the differential if snapping is set | |
this._differential[0] -= newDifferential[0]; | |
this._differential[1] -= newDifferential[1]; | |
var pos = this.getPosition(); | |
//modify position, retain reference | |
pos[0] += newDifferential[0]; | |
pos[1] += newDifferential[1]; | |
//handle bounding box | |
if (options.xRange){ | |
var xRange = [options.xRange[0] + 0.5 * options.snapX, options.xRange[1] - 0.5 * options.snapX]; | |
pos[0] = _clamp(pos[0], xRange); | |
} | |
if (options.yRange){ | |
var yRange = [options.yRange[0] + 0.5 * options.snapY, options.yRange[1] - 0.5 * options.snapY]; | |
pos[1] = _clamp(pos[1], yRange); | |
} | |
this.eventOutput.emit('update', {position : pos}); | |
} | |
function _handleEnd() { | |
if (!this._active) return; | |
this.eventOutput.emit('end', {position : this.getPosition()}); | |
} | |
function _bindEvents() { | |
this.sync.on('start', _handleStart.bind(this)); | |
this.sync.on('update', _handleMove.bind(this)); | |
this.sync.on('end', _handleEnd.bind(this)); | |
} | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options. See constructor. | |
*/ | |
Draggable.prototype.setOptions = function setOptions(options) { | |
var currentOptions = this.options; | |
if (options.projection !== undefined) { | |
var proj = options.projection; | |
this.options.projection = 0; | |
['x', 'y'].forEach(function(val) { | |
if (proj.indexOf(val) !== -1) currentOptions.projection |= _direction[val]; | |
}); | |
} | |
if (options.scale !== undefined) { | |
currentOptions.scale = options.scale; | |
this.sync.setOptions({ | |
scale: options.scale | |
}); | |
} | |
if (options.xRange !== undefined) currentOptions.xRange = options.xRange; | |
if (options.yRange !== undefined) currentOptions.yRange = options.yRange; | |
if (options.snapX !== undefined) currentOptions.snapX = options.snapX; | |
if (options.snapY !== undefined) currentOptions.snapY = options.snapY; | |
}; | |
/** | |
* Get current delta in position from where this draggable started. | |
* | |
* @method getPosition | |
* | |
* @return {array<number>} [x, y] position delta from start. | |
*/ | |
Draggable.prototype.getPosition = function getPosition() { | |
return this._positionState.get(); | |
}; | |
/** | |
* Transition the element to the desired relative position via provided transition. | |
* For example, calling this with [0,0] will not change the position. | |
* Callback will be executed on completion. | |
* | |
* @method setRelativePosition | |
* | |
* @param {array<number>} position end state to which we interpolate | |
* @param {transition} transition transition object specifying how object moves to new position | |
* @param {function} callback zero-argument function to call on observed completion | |
*/ | |
Draggable.prototype.setRelativePosition = function setRelativePosition(position, transition, callback) { | |
var currPos = this.getPosition(); | |
var relativePosition = [currPos[0] + position[0], currPos[1] + position[1]]; | |
this.setPosition(relativePosition, transition, callback); | |
}; | |
/** | |
* Transition the element to the desired absolute position via provided transition. | |
* Callback will be executed on completion. | |
* | |
* @method setPosition | |
* | |
* @param {array<number>} position end state to which we interpolate | |
* @param {transition} transition transition object specifying how object moves to new position | |
* @param {function} callback zero-argument function to call on observed completion | |
*/ | |
Draggable.prototype.setPosition = function setPosition(position, transition, callback) { | |
if (this._positionState.isActive()) this._positionState.halt(); | |
this._positionState.set(position, transition, callback); | |
}; | |
/** | |
* Set this draggable to respond to user input. | |
* | |
* @method activate | |
* | |
*/ | |
Draggable.prototype.activate = function activate() { | |
this._active = true; | |
}; | |
/** | |
* Set this draggable to ignore user input. | |
* | |
* @method deactivate | |
* | |
*/ | |
Draggable.prototype.deactivate = function deactivate() { | |
this._active = false; | |
}; | |
/** | |
* Switch the input response stage between active and inactive. | |
* | |
* @method toggle | |
* | |
*/ | |
Draggable.prototype.toggle = function toggle() { | |
this._active = !this._active; | |
}; | |
/** | |
* 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 | |
*/ | |
Draggable.prototype.modify = function modify(target) { | |
var pos = this.getPosition(); | |
return { | |
transform: Transform.translate(pos[0], pos[1]), | |
target: target | |
}; | |
}; | |
module.exports = Draggable; | |
}); | |
define('famous/modifiers/Fader',['require','exports','module','famous/transitions/Transitionable','famous/core/OptionsManager'],function(require, exports, module) { | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* Modifier that allows you to fade the opacity of affected renderables in and out. | |
* @class Fader | |
* @constructor | |
* @param {Object} [options] options configuration object. | |
* @param {Boolean} [options.cull=false] Stops returning affected renderables up the tree when they're fully faded when true. | |
* @param {Transition} [options.transition=true] The main transition for showing and hiding. | |
* @param {Transition} [options.pulseInTransition=true] Controls the transition to a pulsed state when the Fader instance's pulse | |
* method is called. | |
* @param {Transition} [options.pulseOutTransition=true]Controls the transition back from a pulsed state when the Fader instance's pulse | |
* method is called. | |
* | |
*/ | |
function Fader(options, startState) { | |
this.options = Object.create(Fader.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
if (!startState) startState = 0; | |
this.transitionHelper = new Transitionable(startState); | |
} | |
Fader.DEFAULT_OPTIONS = { | |
cull: false, | |
transition: true, | |
pulseInTransition: true, | |
pulseOutTransition: true | |
}; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options. See constructor. | |
*/ | |
Fader.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
/** | |
* Fully displays the Fader instance's associated renderables. | |
* | |
* @method show | |
* @param {Transition} [transition] The transition that coordinates setting to the new state. | |
* @param {Function} [callback] A callback that executes once you've transitioned to the fully shown state. | |
*/ | |
Fader.prototype.show = function show(transition, callback) { | |
transition = transition || this.options.transition; | |
this.set(1, transition, callback); | |
}; | |
/** | |
* Fully fades the Fader instance's associated renderables. | |
* | |
* @method hide | |
* @param {Transition} [transition] The transition that coordinates setting to the new state. | |
* @param {Function} [callback] A callback that executes once you've transitioned to the fully faded state. | |
*/ | |
Fader.prototype.hide = function hide(transition, callback) { | |
transition = transition || this.options.transition; | |
this.set(0, transition, callback); | |
}; | |
/** | |
* Manually sets the opacity state of the fader to the passed-in one. Executes with an optional | |
* transition and callback. | |
* | |
* @method set | |
* @param {Number} state A number from zero to one: the amount of opacity you want to set to. | |
* @param {Transition} [transition] The transition that coordinates setting to the new state. | |
* @param {Function} [callback] A callback that executes once you've finished executing the pulse. | |
*/ | |
Fader.prototype.set = function set(state, transition, callback) { | |
this.halt(); | |
this.transitionHelper.set(state, transition, callback); | |
}; | |
/** | |
* Halt the transition | |
* | |
* @method halt | |
*/ | |
Fader.prototype.halt = function halt() { | |
this.transitionHelper.halt(); | |
}; | |
/** | |
* Tells you if your Fader instance is above its visibility threshold. | |
* | |
* @method isVisible | |
* @return {Boolean} Whether or not your Fader instance is visible. | |
*/ | |
Fader.prototype.isVisible = function isVisible() { | |
return (this.transitionHelper.get() > 0); | |
}; | |
/** | |
* 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 | |
*/ | |
Fader.prototype.modify = function modify(target) { | |
var currOpacity = this.transitionHelper.get(); | |
if (this.options.cull && !currOpacity) return undefined; | |
else return {opacity: currOpacity, target: target}; | |
}; | |
module.exports = Fader; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/modifiers/ModifierChain',['require','exports','module'],function(require, exports, module) { | |
/** | |
* A class to add and remove a chain of modifiers | |
* at a single point in the render tree | |
* | |
* @class ModifierChain | |
* @constructor | |
*/ | |
function ModifierChain() { | |
this._chain = []; | |
if (arguments.length) this.addModifier.apply(this, arguments); | |
} | |
/** | |
* Add a modifier, or comma separated modifiers, to the modifier chain. | |
* | |
* @method addModifier | |
* | |
* @param {...Modifier*} varargs args list of Modifiers | |
*/ | |
ModifierChain.prototype.addModifier = function addModifier(varargs) { | |
Array.prototype.push.apply(this._chain, arguments); | |
}; | |
/** | |
* Remove a modifier from the modifier chain. | |
* | |
* @method removeModifier | |
* | |
* @param {Modifier} modifier | |
*/ | |
ModifierChain.prototype.removeModifier = function removeModifier(modifier) { | |
var index = this._chain.indexOf(modifier); | |
if (index < 0) return; | |
this._chain.splice(index, 1); | |
}; | |
/** | |
* Return render spec for this Modifier, applying to the provided | |
* target component. This is similar to render() for Surfaces. | |
* | |
* @private | |
* @method modify | |
* | |
* @param {Object} input (already rendered) render spec to | |
* which to apply the transform. | |
* @return {Object} render spec for this Modifier, including the | |
* provided target | |
*/ | |
ModifierChain.prototype.modify = function modify(input) { | |
var chain = this._chain; | |
var result = input; | |
for (var i = 0; i < chain.length; i++) { | |
result = chain[i].modify(result); | |
} | |
return result; | |
}; | |
module.exports = ModifierChain; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/modifiers/StateModifier',['require','exports','module','famous/core/Modifier','famous/core/Transform','famous/transitions/Transitionable','famous/transitions/TransitionableTransform'],function(require, exports, module) { | |
var Modifier = require('famous/core/Modifier'); | |
var Transform = require('famous/core/Transform'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var TransitionableTransform = require('famous/transitions/TransitionableTransform'); | |
/** | |
* A collection of visual changes to be | |
* applied to another renderable component, strongly coupled with the state that defines | |
* those changes. This collection includes a | |
* transform matrix, an opacity constant, a size, an origin specifier, and an alignment specifier. | |
* StateModifier objects can be added to any RenderNode or object | |
* capable of displaying renderables. The StateModifier's children and descendants | |
* are transformed by the amounts specified in the modifier's properties. | |
* | |
* @class StateModifier | |
* @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.align] align adjustment | |
* @param {Array.Number} [options.size] size to apply to descendants | |
*/ | |
function StateModifier(options) { | |
this._transformState = new TransitionableTransform(Transform.identity); | |
this._opacityState = new Transitionable(1); | |
this._originState = new Transitionable([0, 0]); | |
this._alignState = new Transitionable([0, 0]); | |
this._sizeState = new Transitionable([0, 0]); | |
this._modifier = new Modifier({ | |
transform: this._transformState, | |
opacity: this._opacityState, | |
origin: null, | |
align: null, | |
size: null | |
}); | |
this._hasOrigin = false; | |
this._hasAlign = false; | |
this._hasSize = false; | |
if (options) { | |
if (options.transform) this.setTransform(options.transform); | |
if (options.opacity !== undefined) this.setOpacity(options.opacity); | |
if (options.origin) this.setOrigin(options.origin); | |
if (options.align) this.setAlign(options.align); | |
if (options.size) this.setSize(options.size); | |
} | |
} | |
/** | |
* Set the transform matrix of this modifier, either statically or | |
* through a provided Transitionable. | |
* | |
* @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 {StateModifier} this | |
*/ | |
StateModifier.prototype.setTransform = function setTransform(transform, transition, callback) { | |
this._transformState.set(transform, transition, callback); | |
return this; | |
}; | |
/** | |
* Set the opacity of this modifier, either statically or | |
* through a provided Transitionable. | |
* | |
* @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 {StateModifier} this | |
*/ | |
StateModifier.prototype.setOpacity = function setOpacity(opacity, transition, callback) { | |
this._opacityState.set(opacity, transition, callback); | |
return this; | |
}; | |
/** | |
* Set the origin of this modifier, either statically or | |
* through a provided Transitionable. | |
* | |
* @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 {StateModifier} this | |
*/ | |
StateModifier.prototype.setOrigin = function setOrigin(origin, transition, callback) { | |
if (origin === null) { | |
if (this._hasOrigin) { | |
this._modifier.originFrom(null); | |
this._hasOrigin = false; | |
} | |
return this; | |
} | |
else if (!this._hasOrigin) { | |
this._hasOrigin = true; | |
this._modifier.originFrom(this._originState); | |
} | |
this._originState.set(origin, transition, callback); | |
return this; | |
}; | |
/** | |
* Set the alignment of this modifier, either statically or | |
* through a provided Transitionable. | |
* | |
* @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 {StateModifier} this | |
*/ | |
StateModifier.prototype.setAlign = function setOrigin(align, transition, callback) { | |
if (align === null) { | |
if (this._hasAlign) { | |
this._modifier.alignFrom(null); | |
this._hasAlign = false; | |
} | |
return this; | |
} | |
else if (!this._hasAlign) { | |
this._hasAlign = true; | |
this._modifier.alignFrom(this._alignState); | |
} | |
this._alignState.set(align, transition, callback); | |
return this; | |
}; | |
/** | |
* Set the size of this modifier, either statically or | |
* through a provided Transitionable. | |
* | |
* @method setSize | |
* | |
* @param {Array.Number} size 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 {StateModifier} this | |
*/ | |
StateModifier.prototype.setSize = function setSize(size, transition, callback) { | |
if (size === null) { | |
if (this._hasSize) { | |
this._modifier.sizeFrom(null); | |
this._hasSize = false; | |
} | |
return this; | |
} | |
else if (!this._hasSize) { | |
this._hasSize = true; | |
this._modifier.sizeFrom(this._sizeState); | |
} | |
this._sizeState.set(size, transition, callback); | |
return this; | |
}; | |
/** | |
* Stop the transition. | |
* | |
* @method halt | |
*/ | |
StateModifier.prototype.halt = function halt() { | |
this._transformState.halt(); | |
this._opacityState.halt(); | |
this._originState.halt(); | |
this._alignState.halt(); | |
this._sizeState.halt(); | |
}; | |
/** | |
* Get the current state of the transform matrix component. | |
* | |
* @method getTransform | |
* @return {Object} transform provider object | |
*/ | |
StateModifier.prototype.getTransform = function getTransform() { | |
return this._transformState.get(); | |
}; | |
/** | |
* Get the destination state of the transform component. | |
* | |
* @method getFinalTransform | |
* @return {Transform} transform matrix | |
*/ | |
StateModifier.prototype.getFinalTransform = function getFinalTransform() { | |
return this._transformState.getFinal(); | |
}; | |
/** | |
* Get the current state of the opacity component. | |
* | |
* @method getOpacity | |
* @return {Object} opacity provider object | |
*/ | |
StateModifier.prototype.getOpacity = function getOpacity() { | |
return this._opacityState.get(); | |
}; | |
/** | |
* Get the current state of the origin component. | |
* | |
* @method getOrigin | |
* @return {Object} origin provider object | |
*/ | |
StateModifier.prototype.getOrigin = function getOrigin() { | |
return this._hasOrigin ? this._originState.get() : null; | |
}; | |
/** | |
* Get the current state of the align component. | |
* | |
* @method getAlign | |
* @return {Object} align provider object | |
*/ | |
StateModifier.prototype.getAlign = function getAlign() { | |
return this._hasAlign ? this._alignState.get() : null; | |
}; | |
/** | |
* Get the current state of the size component. | |
* | |
* @method getSize | |
* @return {Object} size provider object | |
*/ | |
StateModifier.prototype.getSize = function getSize() { | |
return this._hasSize ? this._sizeState.get() : null; | |
}; | |
/** | |
* Return render spec for this StateModifier, 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 StateModifier, including the | |
* provided target | |
*/ | |
StateModifier.prototype.modify = function modify(target) { | |
return this._modifier.modify(target); | |
}; | |
module.exports = StateModifier; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/math/Vector',['require','exports','module'],function(require, exports, module) { | |
/** | |
* Three-element floating point vector. | |
* | |
* @class Vector | |
* @constructor | |
* | |
* @param {number} x x element value | |
* @param {number} y y element value | |
* @param {number} z z element value | |
*/ | |
function Vector(x,y,z) { | |
if (arguments.length === 1) this.set(x); | |
else { | |
this.x = x || 0; | |
this.y = y || 0; | |
this.z = z || 0; | |
} | |
return this; | |
} | |
var _register = new Vector(0,0,0); | |
/** | |
* Add this element-wise to another Vector, element-wise. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method add | |
* @param {Vector} v addend | |
* @return {Vector} vector sum | |
*/ | |
Vector.prototype.add = function add(v) { | |
return _setXYZ.call(_register, | |
this.x + v.x, | |
this.y + v.y, | |
this.z + v.z | |
); | |
}; | |
/** | |
* Subtract another vector from this vector, element-wise. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method sub | |
* @param {Vector} v subtrahend | |
* @return {Vector} vector difference | |
*/ | |
Vector.prototype.sub = function sub(v) { | |
return _setXYZ.call(_register, | |
this.x - v.x, | |
this.y - v.y, | |
this.z - v.z | |
); | |
}; | |
/** | |
* Scale Vector by floating point r. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method mult | |
* | |
* @param {number} r scalar | |
* @return {Vector} vector result | |
*/ | |
Vector.prototype.mult = function mult(r) { | |
return _setXYZ.call(_register, | |
r * this.x, | |
r * this.y, | |
r * this.z | |
); | |
}; | |
/** | |
* Scale Vector by floating point 1/r. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method div | |
* | |
* @param {number} r scalar | |
* @return {Vector} vector result | |
*/ | |
Vector.prototype.div = function div(r) { | |
return this.mult(1 / r); | |
}; | |
/** | |
* Given another vector v, return cross product (v)x(this). | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method cross | |
* @param {Vector} v Left Hand Vector | |
* @return {Vector} vector result | |
*/ | |
Vector.prototype.cross = function cross(v) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var vx = v.x; | |
var vy = v.y; | |
var vz = v.z; | |
return _setXYZ.call(_register, | |
z * vy - y * vz, | |
x * vz - z * vx, | |
y * vx - x * vy | |
); | |
}; | |
/** | |
* Component-wise equality test between this and Vector v. | |
* @method equals | |
* @param {Vector} v vector to compare | |
* @return {boolean} | |
*/ | |
Vector.prototype.equals = function equals(v) { | |
return (v.x === this.x && v.y === this.y && v.z === this.z); | |
}; | |
/** | |
* Rotate clockwise around x-axis by theta radians. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* @method rotateX | |
* @param {number} theta radians | |
* @return {Vector} rotated vector | |
*/ | |
Vector.prototype.rotateX = function rotateX(theta) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var cosTheta = Math.cos(theta); | |
var sinTheta = Math.sin(theta); | |
return _setXYZ.call(_register, | |
x, | |
y * cosTheta - z * sinTheta, | |
y * sinTheta + z * cosTheta | |
); | |
}; | |
/** | |
* Rotate clockwise around y-axis by theta radians. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* @method rotateY | |
* @param {number} theta radians | |
* @return {Vector} rotated vector | |
*/ | |
Vector.prototype.rotateY = function rotateY(theta) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var cosTheta = Math.cos(theta); | |
var sinTheta = Math.sin(theta); | |
return _setXYZ.call(_register, | |
z * sinTheta + x * cosTheta, | |
y, | |
z * cosTheta - x * sinTheta | |
); | |
}; | |
/** | |
* Rotate clockwise around z-axis by theta radians. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* @method rotateZ | |
* @param {number} theta radians | |
* @return {Vector} rotated vector | |
*/ | |
Vector.prototype.rotateZ = function rotateZ(theta) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var cosTheta = Math.cos(theta); | |
var sinTheta = Math.sin(theta); | |
return _setXYZ.call(_register, | |
x * cosTheta - y * sinTheta, | |
x * sinTheta + y * cosTheta, | |
z | |
); | |
}; | |
/** | |
* Return dot product of this with a second Vector | |
* @method dot | |
* @param {Vector} v second vector | |
* @return {number} dot product | |
*/ | |
Vector.prototype.dot = function dot(v) { | |
return this.x * v.x + this.y * v.y + this.z * v.z; | |
}; | |
/** | |
* Return squared length of this vector | |
* @method normSquared | |
* @return {number} squared length | |
*/ | |
Vector.prototype.normSquared = function normSquared() { | |
return this.dot(this); | |
}; | |
/** | |
* Return length of this vector | |
* @method norm | |
* @return {number} length | |
*/ | |
Vector.prototype.norm = function norm() { | |
return Math.sqrt(this.normSquared()); | |
}; | |
/** | |
* Scale Vector to specified length. | |
* If length is less than internal tolerance, set vector to [length, 0, 0]. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* @method normalize | |
* | |
* @param {number} length target length, default 1.0 | |
* @return {Vector} | |
*/ | |
Vector.prototype.normalize = function normalize(length) { | |
if (arguments.length === 0) length = 1; | |
var norm = this.norm(); | |
if (norm > 1e-7) return _setFromVector.call(_register, this.mult(length / norm)); | |
else return _setXYZ.call(_register, length, 0, 0); | |
}; | |
/** | |
* Make a separate copy of the Vector. | |
* | |
* @method clone | |
* | |
* @return {Vector} | |
*/ | |
Vector.prototype.clone = function clone() { | |
return new Vector(this); | |
}; | |
/** | |
* True if and only if every value is 0 (or falsy) | |
* | |
* @method isZero | |
* | |
* @return {boolean} | |
*/ | |
Vector.prototype.isZero = function isZero() { | |
return !(this.x || this.y || this.z); | |
}; | |
function _setXYZ(x,y,z) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
return this; | |
} | |
function _setFromArray(v) { | |
return _setXYZ.call(this,v[0],v[1],v[2] || 0); | |
} | |
function _setFromVector(v) { | |
return _setXYZ.call(this, v.x, v.y, v.z); | |
} | |
function _setFromNumber(x) { | |
return _setXYZ.call(this,x,0,0); | |
} | |
/** | |
* Set this Vector to the values in the provided Array or Vector. | |
* | |
* @method set | |
* @param {object} v array, Vector, or number | |
* @return {Vector} this | |
*/ | |
Vector.prototype.set = function set(v) { | |
if (v instanceof Array) return _setFromArray.call(this, v); | |
if (v instanceof Vector) return _setFromVector.call(this, v); | |
if (typeof v === 'number') return _setFromNumber.call(this, v); | |
}; | |
Vector.prototype.setXYZ = function(x,y,z) { | |
return _setXYZ.apply(this, arguments); | |
}; | |
Vector.prototype.set1D = function(x) { | |
return _setFromNumber.call(this, x); | |
}; | |
/** | |
* Put result of last internal register calculation in specified output vector. | |
* | |
* @method put | |
* @param {Vector} v destination vector | |
* @return {Vector} destination vector | |
*/ | |
Vector.prototype.put = function put(v) { | |
_setFromVector.call(v, _register); | |
}; | |
/** | |
* Set this vector to [0,0,0] | |
* | |
* @method clear | |
*/ | |
Vector.prototype.clear = function clear() { | |
return _setXYZ.call(this,0,0,0); | |
}; | |
/** | |
* Scale this Vector down to specified "cap" length. | |
* If Vector shorter than cap, or cap is Infinity, do nothing. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method cap | |
* @return {Vector} capped vector | |
*/ | |
Vector.prototype.cap = function cap(cap) { | |
if (cap === Infinity) return _setFromVector.call(_register, this); | |
var norm = this.norm(); | |
if (norm > cap) return _setFromVector.call(_register, this.mult(cap / norm)); | |
else return _setFromVector.call(_register, this); | |
}; | |
/** | |
* Return projection of this Vector onto another. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method project | |
* @param {Vector} n vector to project upon | |
* @return {Vector} projected vector | |
*/ | |
Vector.prototype.project = function project(n) { | |
return n.mult(this.dot(n)); | |
}; | |
/** | |
* Reflect this Vector across provided vector. | |
* Note: This sets the internal result register, so other references to that vector will change. | |
* | |
* @method reflectAcross | |
* @param {Vector} n vector to reflect across | |
* @return {Vector} reflected vector | |
*/ | |
Vector.prototype.reflectAcross = function reflectAcross(n) { | |
n.normalize().put(n); | |
return _setFromVector(_register, this.sub(this.project(n).mult(2))); | |
}; | |
/** | |
* Convert Vector to three-element array. | |
* | |
* @method get | |
* @return {array<number>} three-element array | |
*/ | |
Vector.prototype.get = function get() { | |
return [this.x, this.y, this.z]; | |
}; | |
Vector.prototype.get1D = function() { | |
return this.x; | |
}; | |
module.exports = Vector; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/math/Matrix',['require','exports','module','./Vector'],function(require, exports, module) { | |
var Vector = require('./Vector'); | |
/** | |
* A library for using a 3x3 numerical matrix, represented as a two-level array. | |
* | |
* @class Matrix | |
* @constructor | |
* | |
* @param {Array.Array} values array of rows | |
*/ | |
function Matrix(values) { | |
this.values = values || | |
[ | |
[1,0,0], | |
[0,1,0], | |
[0,0,1] | |
]; | |
return this; | |
} | |
var _register = new Matrix(); | |
var _vectorRegister = new Vector(); | |
/** | |
* Return the values in the matrix as an array of numerical row arrays | |
* | |
* @method get | |
* | |
* @return {Array.array} matrix values as array of rows. | |
*/ | |
Matrix.prototype.get = function get() { | |
return this.values; | |
}; | |
/** | |
* Set the nested array of rows in the matrix. | |
* | |
* @method set | |
* | |
* @param {Array.array} values matrix values as array of rows. | |
*/ | |
Matrix.prototype.set = function set(values) { | |
this.values = values; | |
}; | |
/** | |
* Take this matrix as A, input vector V as a column vector, and return matrix product (A)(V). | |
* Note: This sets the internal vector register. Current handles to the vector register | |
* will see values changed. | |
* | |
* @method vectorMultiply | |
* | |
* @param {Vector} v input vector V | |
* @return {Vector} result of multiplication, as a handle to the internal vector register | |
*/ | |
Matrix.prototype.vectorMultiply = function vectorMultiply(v) { | |
var M = this.get(); | |
var v0 = v.x; | |
var v1 = v.y; | |
var v2 = v.z; | |
var M0 = M[0]; | |
var M1 = M[1]; | |
var M2 = M[2]; | |
var M00 = M0[0]; | |
var M01 = M0[1]; | |
var M02 = M0[2]; | |
var M10 = M1[0]; | |
var M11 = M1[1]; | |
var M12 = M1[2]; | |
var M20 = M2[0]; | |
var M21 = M2[1]; | |
var M22 = M2[2]; | |
return _vectorRegister.setXYZ( | |
M00*v0 + M01*v1 + M02*v2, | |
M10*v0 + M11*v1 + M12*v2, | |
M20*v0 + M21*v1 + M22*v2 | |
); | |
}; | |
/** | |
* Multiply the provided matrix M2 with this matrix. Result is (this) * (M2). | |
* Note: This sets the internal matrix register. Current handles to the register | |
* will see values changed. | |
* | |
* @method multiply | |
* | |
* @param {Matrix} M2 input matrix to multiply on the right | |
* @return {Matrix} result of multiplication, as a handle to the internal register | |
*/ | |
Matrix.prototype.multiply = function multiply(M2) { | |
var M1 = this.get(); | |
var result = [[]]; | |
for (var i = 0; i < 3; i++) { | |
result[i] = []; | |
for (var j = 0; j < 3; j++) { | |
var sum = 0; | |
for (var k = 0; k < 3; k++) { | |
sum += M1[i][k] * M2[k][j]; | |
} | |
result[i][j] = sum; | |
} | |
} | |
return _register.set(result); | |
}; | |
/** | |
* Creates a Matrix which is the transpose of this matrix. | |
* Note: This sets the internal matrix register. Current handles to the register | |
* will see values changed. | |
* | |
* @method transpose | |
* | |
* @return {Matrix} result of transpose, as a handle to the internal register | |
*/ | |
Matrix.prototype.transpose = function transpose() { | |
var result = []; | |
var M = this.get(); | |
for (var row = 0; row < 3; row++) { | |
for (var col = 0; col < 3; col++) { | |
result[row][col] = M[col][row]; | |
} | |
} | |
return _register.set(result); | |
}; | |
/** | |
* Clones the matrix | |
* | |
* @method clone | |
* @return {Matrix} New copy of the original matrix | |
*/ | |
Matrix.prototype.clone = function clone() { | |
var values = this.get(); | |
var M = []; | |
for (var row = 0; row < 3; row++) | |
M[row] = values[row].slice(); | |
return new Matrix(M); | |
}; | |
module.exports = Matrix; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/math/Quaternion',['require','exports','module','./Matrix'],function(require, exports, module) { | |
var Matrix = require('./Matrix'); | |
/** | |
* @class Quaternion | |
* @constructor | |
* | |
* @param {Number} w | |
* @param {Number} x | |
* @param {Number} y | |
* @param {Number} z | |
*/ | |
function Quaternion(w,x,y,z) { | |
if (arguments.length === 1) this.set(w); | |
else { | |
this.w = (w !== undefined) ? w : 1; //Angle | |
this.x = (x !== undefined) ? x : 0; //Axis.x | |
this.y = (y !== undefined) ? y : 0; //Axis.y | |
this.z = (z !== undefined) ? z : 0; //Axis.z | |
} | |
return this; | |
} | |
var register = new Quaternion(1,0,0,0); | |
/** | |
* Doc: TODO | |
* @method add | |
* @param {Quaternion} q | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.add = function add(q) { | |
return register.setWXYZ( | |
this.w + q.w, | |
this.x + q.x, | |
this.y + q.y, | |
this.z + q.z | |
); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method sub | |
* @param {Quaternion} q | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.sub = function sub(q) { | |
return register.setWXYZ( | |
this.w - q.w, | |
this.x - q.x, | |
this.y - q.y, | |
this.z - q.z | |
); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method scalarDivide | |
* @param {Number} s | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.scalarDivide = function scalarDivide(s) { | |
return this.scalarMultiply(1/s); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method scalarMultiply | |
* @param {Number} s | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.scalarMultiply = function scalarMultiply(s) { | |
return register.setWXYZ( | |
this.w * s, | |
this.x * s, | |
this.y * s, | |
this.z * s | |
); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method multiply | |
* @param {Quaternion} q | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.multiply = function multiply(q) { | |
//left-handed coordinate system multiplication | |
var x1 = this.x; | |
var y1 = this.y; | |
var z1 = this.z; | |
var w1 = this.w; | |
var x2 = q.x; | |
var y2 = q.y; | |
var z2 = q.z; | |
var w2 = q.w || 0; | |
return register.setWXYZ( | |
w1*w2 - x1*x2 - y1*y2 - z1*z2, | |
x1*w2 + x2*w1 + y2*z1 - y1*z2, | |
y1*w2 + y2*w1 + x1*z2 - x2*z1, | |
z1*w2 + z2*w1 + x2*y1 - x1*y2 | |
); | |
}; | |
var conj = new Quaternion(1,0,0,0); | |
/* | |
* Docs: TODO | |
* | |
* @method rotateVector | |
* @param {Vector} v | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.rotateVector = function rotateVector(v) { | |
conj.set(this.conj()); | |
return register.set(this.multiply(v).multiply(conj)); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method inverse | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.inverse = function inverse() { | |
return register.set(this.conj().scalarDivide(this.normSquared())); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method negate | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.negate = function negate() { | |
return this.scalarMultiply(-1); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method conj | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.conj = function conj() { | |
return register.setWXYZ( | |
this.w, | |
-this.x, | |
-this.y, | |
-this.z | |
); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method normalize | |
* @param {Number} length | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.normalize = function normalize(length) { | |
length = (length === undefined) ? 1 : length; | |
return this.scalarDivide(length * this.norm()); | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method makeFromAngleAndAxis | |
* @param {Number} angle | |
* @param {Vector} v | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.makeFromAngleAndAxis = function makeFromAngleAndAxis(angle, v) { | |
//left handed quaternion creation: theta -> -theta | |
var n = v.normalize(); | |
var ha = angle*0.5; | |
var s = -Math.sin(ha); | |
this.x = s*n.x; | |
this.y = s*n.y; | |
this.z = s*n.z; | |
this.w = Math.cos(ha); | |
return this; | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method setWXYZ | |
* @param {Number} w | |
* @param {Number} x | |
* @param {Number} y | |
* @param {Number} z | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.setWXYZ = function setWXYZ(w,x,y,z) { | |
register.clear(); | |
this.w = w; | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
return this; | |
}; | |
/* | |
* Docs: TODO | |
* | |
* @method set | |
* @param {Array|Quaternion} v | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.set = function set(v) { | |
if (v instanceof Array) { | |
this.w = v[0]; | |
this.x = v[1]; | |
this.y = v[2]; | |
this.z = v[3]; | |
} | |
else { | |
this.w = v.w; | |
this.x = v.x; | |
this.y = v.y; | |
this.z = v.z; | |
} | |
if (this !== register) register.clear(); | |
return this; | |
}; | |
/** | |
* Docs: TODO | |
* | |
* @method put | |
* @param {Quaternion} q | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.put = function put(q) { | |
q.set(register); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method clone | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.clone = function clone() { | |
return new Quaternion(this); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method clear | |
* @return {Quaternion} | |
*/ | |
Quaternion.prototype.clear = function clear() { | |
this.w = 1; | |
this.x = 0; | |
this.y = 0; | |
this.z = 0; | |
return this; | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method isEqual | |
* @param {Quaternion} q | |
* @return {Boolean} | |
*/ | |
Quaternion.prototype.isEqual = function isEqual(q) { | |
return q.w === this.w && q.x === this.x && q.y === this.y && q.z === this.z; | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method dot | |
* @param {Quaternion} q | |
* @return {Number} | |
*/ | |
Quaternion.prototype.dot = function dot(q) { | |
return this.w * q.w + this.x * q.x + this.y * q.y + this.z * q.z; | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method normSquared | |
* @return {Number} | |
*/ | |
Quaternion.prototype.normSquared = function normSquared() { | |
return this.dot(this); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method norm | |
* @return {Number} | |
*/ | |
Quaternion.prototype.norm = function norm() { | |
return Math.sqrt(this.normSquared()); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method isZero | |
* @return {Boolean} | |
*/ | |
Quaternion.prototype.isZero = function isZero() { | |
return !(this.x || this.y || this.z); | |
}; | |
/** | |
* Doc: TODO | |
* | |
* @method getTransform | |
* @return {Transform} | |
*/ | |
Quaternion.prototype.getTransform = function getTransform() { | |
var temp = this.normalize(1); | |
var x = temp.x; | |
var y = temp.y; | |
var z = temp.z; | |
var w = temp.w; | |
//LHC system flattened to column major = RHC flattened to row major | |
return [ | |
1 - 2*y*y - 2*z*z, | |
2*x*y - 2*z*w, | |
2*x*z + 2*y*w, | |
0, | |
2*x*y + 2*z*w, | |
1 - 2*x*x - 2*z*z, | |
2*y*z - 2*x*w, | |
0, | |
2*x*z - 2*y*w, | |
2*y*z + 2*x*w, | |
1 - 2*x*x - 2*y*y, | |
0, | |
0, | |
0, | |
0, | |
1 | |
]; | |
}; | |
var matrixRegister = new Matrix(); | |
/** | |
* Doc: TODO | |
* | |
* @method getMatrix | |
* @return {Transform} | |
*/ | |
Quaternion.prototype.getMatrix = function getMatrix() { | |
var temp = this.normalize(1); | |
var x = temp.x; | |
var y = temp.y; | |
var z = temp.z; | |
var w = temp.w; | |
//LHC system flattened to row major | |
return matrixRegister.set([ | |
[ | |
1 - 2*y*y - 2*z*z, | |
2*x*y + 2*z*w, | |
2*x*z - 2*y*w | |
], | |
[ | |
2*x*y - 2*z*w, | |
1 - 2*x*x - 2*z*z, | |
2*y*z + 2*x*w | |
], | |
[ | |
2*x*z + 2*y*w, | |
2*y*z - 2*x*w, | |
1 - 2*x*x - 2*y*y | |
] | |
]); | |
}; | |
var epsilon = 1e-5; | |
/** | |
* Doc: TODO | |
* | |
* @method slerp | |
* @param {Quaternion} q | |
* @param {Number} t | |
* @return {Transform} | |
*/ | |
Quaternion.prototype.slerp = function slerp(q, t) { | |
var omega; | |
var cosomega; | |
var sinomega; | |
var scaleFrom; | |
var scaleTo; | |
cosomega = this.dot(q); | |
if ((1.0 - cosomega) > epsilon) { | |
omega = Math.acos(cosomega); | |
sinomega = Math.sin(omega); | |
scaleFrom = Math.sin((1.0 - t) * omega) / sinomega; | |
scaleTo = Math.sin(t * omega) / sinomega; | |
} | |
else { | |
scaleFrom = 1.0 - t; | |
scaleTo = t; | |
} | |
return register.set(this.scalarMultiply(scaleFrom/scaleTo).add(q).multiply(scaleTo)); | |
}; | |
module.exports = Quaternion; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/math/Random',['require','exports','module'],function(require, exports, module) { | |
var RAND = Math.random; | |
function _randomFloat(min,max) { | |
return min + RAND() * (max - min); | |
} | |
function _randomInteger(min,max) { | |
return (min + RAND() * (max - min + 1)) >> 0; | |
} | |
/** | |
* Very simple uniform random number generator library wrapping Math.random(). | |
* | |
* @class Random | |
* @static | |
*/ | |
var Random = {}; | |
/** | |
* Get single random integer between min and max (inclusive), or array | |
* of size dim if specified. | |
* | |
* @method integer | |
* | |
* @param {Number} min lower bound, default 0 | |
* @param {Number} max upper bound, default 1 | |
* @param {Number} dim (optional) dimension of output array, if specified | |
* @return {number | array<number>} random integer, or optionally, an array of random integers | |
*/ | |
Random.integer = function integer(min,max,dim) { | |
min = (min !== undefined) ? min : 0; | |
max = (max !== undefined) ? max : 1; | |
if (dim !== undefined) { | |
var result = []; | |
for (var i = 0; i < dim; i++) result.push(_randomInteger(min,max)); | |
return result; | |
} | |
else return _randomInteger(min,max); | |
}; | |
/** | |
* Get single random float between min and max (inclusive), or array | |
* of size dim if specified | |
* | |
* @method range | |
* | |
* @param {Number} min lower bound, default 0 | |
* @param {Number} max upper bound, default 1 | |
* @param {Number} [dim] dimension of output array, if specified | |
* @return {Number} random float, or optionally an array | |
*/ | |
Random.range = function range(min,max,dim) { | |
min = (min !== undefined) ? min : 0; | |
max = (max !== undefined) ? max : 1; | |
if (dim !== undefined) { | |
var result = []; | |
for (var i = 0; i < dim; i++) result.push(_randomFloat(min,max)); | |
return result; | |
} | |
else return _randomFloat(min,max); | |
}; | |
/** | |
* Return random number among the set {-1 ,1} | |
* | |
* @method sign | |
* | |
* @param {Number} prob probability of returning 1, default 0.5 | |
* @return {Number} random sign (-1 or 1) | |
*/ | |
Random.sign = function sign(prob) { | |
prob = (prob !== undefined) ? prob : 0.5; | |
return (RAND() < prob) ? 1 : -1; | |
}; | |
/** | |
* Return random boolean value, true or false. | |
* | |
* @method bool | |
* | |
* @param {Number} prob probability of returning true, default 0.5 | |
* @return {Boolean} random boolean | |
*/ | |
Random.bool = function bool(prob) { | |
prob = (prob !== undefined) ? prob : 0.5; | |
return RAND() < prob; | |
}; | |
module.exports = Random; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/events/EventArbiter',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* A switch which wraps several event destinations and | |
* redirects received events to at most one of them. | |
* Setting the 'mode' of the object dictates which one | |
* of these destinations will receive events. | |
* | |
* @class EventArbiter | |
* @constructor | |
* | |
* @param {Number | string} startMode initial setting of switch, | |
*/ | |
function EventArbiter(startMode) { | |
this.dispatchers = {}; | |
this.currMode = undefined; | |
this.setMode(startMode); | |
} | |
/** | |
* Set switch to this mode, passing events to the corresponding | |
* EventHandler. If mode has changed, emits 'change' event, | |
* emits 'unpipe' event to the old mode's handler, and emits 'pipe' | |
* event to the new mode's handler. | |
* | |
* @method setMode | |
* | |
* @param {string | number} mode indicating which event handler to send to. | |
*/ | |
EventArbiter.prototype.setMode = function setMode(mode) { | |
if (mode !== this.currMode) { | |
var startMode = this.currMode; | |
if (this.dispatchers[this.currMode]) this.dispatchers[this.currMode].trigger('unpipe'); | |
this.currMode = mode; | |
if (this.dispatchers[mode]) this.dispatchers[mode].emit('pipe'); | |
this.emit('change', {from: startMode, to: mode}); | |
} | |
}; | |
/** | |
* Return the existing EventHandler corresponding to this | |
* mode, creating one if it doesn't exist. | |
* | |
* @method forMode | |
* | |
* @param {string | number} mode mode to which this eventHandler corresponds | |
* | |
* @return {EventHandler} eventHandler corresponding to this mode | |
*/ | |
EventArbiter.prototype.forMode = function forMode(mode) { | |
if (!this.dispatchers[mode]) this.dispatchers[mode] = new EventHandler(); | |
return this.dispatchers[mode]; | |
}; | |
/** | |
* Trigger an event, sending to currently selected handler, if | |
* it is listening for provided 'type' key. | |
* | |
* @method emit | |
* | |
* @param {string} eventType event type key (for example, 'click') | |
* @param {Object} event event data | |
* @return {EventHandler} this | |
*/ | |
EventArbiter.prototype.emit = function emit(eventType, event) { | |
if (this.currMode === undefined) return false; | |
if (!event) event = {}; | |
var dispatcher = this.dispatchers[this.currMode]; | |
if (dispatcher) return dispatcher.trigger(eventType, event); | |
}; | |
module.exports = EventArbiter; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/events/EventFilter',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* EventFilter regulates the broadcasting of events based on | |
* a specified condition function of standard event type: function(type, data). | |
* | |
* @class EventFilter | |
* @constructor | |
* | |
* @param {function} condition function to determine whether or not | |
* events are emitted. | |
*/ | |
function EventFilter(condition) { | |
EventHandler.call(this); | |
this._condition = condition; | |
} | |
EventFilter.prototype = Object.create(EventHandler.prototype); | |
EventFilter.prototype.constructor = EventFilter; | |
/** | |
* If filter condition is met, 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} data event data | |
* @return {EventHandler} this | |
*/ | |
EventFilter.prototype.emit = function emit(type, data) { | |
if (this._condition(type, data)) | |
return EventHandler.prototype.emit.apply(this, arguments); | |
}; | |
/** | |
* An alias of emit. Trigger determines whether to send | |
* events based on the return value of it's condition function | |
* when passed the event type and associated data. | |
* | |
* @method trigger | |
* @param {string} type name of the event | |
* @param {object} data associated data | |
*/ | |
EventFilter.prototype.trigger = EventFilter.prototype.emit; | |
module.exports = EventFilter; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/events/EventMapper',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* EventMapper routes events to various event destinations | |
* based on custom logic. The function signature is arbitrary. | |
* | |
* @class EventMapper | |
* @constructor | |
* | |
* @param {function} mappingFunction function to determine where | |
* events are routed to. | |
*/ | |
function EventMapper(mappingFunction) { | |
EventHandler.call(this); | |
this._mappingFunction = mappingFunction; | |
} | |
EventMapper.prototype = Object.create(EventHandler.prototype); | |
EventMapper.prototype.constructor = EventMapper; | |
EventMapper.prototype.subscribe = null; | |
EventMapper.prototype.unsubscribe = null; | |
/** | |
* Trigger an event, sending to all mapped downstream handlers | |
* listening for provided 'type' key. | |
* | |
* @method emit | |
* | |
* @param {string} type event type key (for example, 'click') | |
* @param {Object} data event data | |
* @return {EventHandler} this | |
*/ | |
EventMapper.prototype.emit = function emit(type, data) { | |
var target = this._mappingFunction.apply(this, arguments); | |
if (target && (target.emit instanceof Function)) target.emit(type, data); | |
}; | |
/** | |
* Alias of emit. | |
* @method trigger | |
*/ | |
EventMapper.prototype.trigger = EventMapper.prototype.emit; | |
module.exports = EventMapper; | |
}); | |
/* 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/. | |
* | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/PhysicsEngine',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* The Physics Engine is responsible for mediating Bodies and their | |
* interaction with forces and constraints. The Physics Engine handles the | |
* logic of adding and removing bodies, updating their state of the over | |
* time. | |
* | |
* @class PhysicsEngine | |
* @constructor | |
* @param options {Object} options | |
*/ | |
function PhysicsEngine(options) { | |
this.options = Object.create(PhysicsEngine.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._particles = []; //list of managed particles | |
this._bodies = []; //list of managed bodies | |
this._agents = {}; //hash of managed agents | |
this._forces = []; //list of IDs of agents that are forces | |
this._constraints = []; //list of IDs of agents that are constraints | |
this._buffer = 0.0; | |
this._prevTime = now(); | |
this._isSleeping = false; | |
this._eventHandler = null; | |
this._currAgentId = 0; | |
this._hasBodies = false; | |
} | |
var TIMESTEP = 17; | |
var MIN_TIME_STEP = 1000 / 120; | |
var MAX_TIME_STEP = 17; | |
/** | |
* @property PhysicsEngine.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
PhysicsEngine.DEFAULT_OPTIONS = { | |
/** | |
* The number of iterations the engine takes to resolve constraints | |
* @attribute constraintSteps | |
* @type Number | |
*/ | |
constraintSteps : 1, | |
/** | |
* The energy threshold before the Engine stops updating | |
* @attribute sleepTolerance | |
* @type Number | |
*/ | |
sleepTolerance : 1e-7 | |
}; | |
var now = (function() { | |
return Date.now; | |
})(); | |
/** | |
* Options setter | |
* @method setOptions | |
* @param options {Object} | |
*/ | |
PhysicsEngine.prototype.setOptions = function setOptions(opts) { | |
for (var key in opts) if (this.options[key]) this.options[key] = opts[key]; | |
}; | |
/** | |
* Method to add a physics body to the engine. Necessary to update the | |
* body over time. | |
* | |
* @method addBody | |
* @param body {Body} | |
* @return body {Body} | |
*/ | |
PhysicsEngine.prototype.addBody = function addBody(body) { | |
body._engine = this; | |
if (body.isBody) { | |
this._bodies.push(body); | |
this._hasBodies = true; | |
} | |
else this._particles.push(body); | |
return body; | |
}; | |
/** | |
* Remove a body from the engine. Detaches body from all forces and | |
* constraints. | |
* | |
* @method removeBody | |
* @param body {Body} | |
*/ | |
PhysicsEngine.prototype.removeBody = function removeBody(body) { | |
var array = (body.isBody) ? this._bodies : this._particles; | |
var index = array.indexOf(body); | |
if (index > -1) { | |
for (var i = 0; i < Object.keys(this._agents).length; i++) this.detachFrom(i, body); | |
array.splice(index,1); | |
} | |
if (this.getBodies().length === 0) this._hasBodies = false; | |
}; | |
function _mapAgentArray(agent) { | |
if (agent.applyForce) return this._forces; | |
if (agent.applyConstraint) return this._constraints; | |
} | |
function _attachOne(agent, targets, source) { | |
if (targets === undefined) targets = this.getParticlesAndBodies(); | |
if (!(targets instanceof Array)) targets = [targets]; | |
this._agents[this._currAgentId] = { | |
agent : agent, | |
targets : targets, | |
source : source | |
}; | |
_mapAgentArray.call(this, agent).push(this._currAgentId); | |
return this._currAgentId++; | |
} | |
/** | |
* Attaches a force or constraint to a Body. Returns an AgentId of the | |
* attached agent which can be used to detach the agent. | |
* | |
* @method attach | |
* @param agent {Agent|Array.Agent} A force, constraint, or array of them. | |
* @param [targets=All] {Body|Array.Body} The Body or Bodies affected by the agent | |
* @param [source] {Body} The source of the agent | |
* @return AgentId {Number} | |
*/ | |
PhysicsEngine.prototype.attach = function attach(agents, targets, source) { | |
if (agents instanceof Array) { | |
var agentIDs = []; | |
for (var i = 0; i < agents.length; i++) | |
agentIDs[i] = _attachOne.call(this, agents[i], targets, source); | |
return agentIDs; | |
} | |
else return _attachOne.call(this, agents, targets, source); | |
}; | |
/** | |
* Append a body to the targets of a previously defined physics agent. | |
* | |
* @method attachTo | |
* @param agentID {AgentId} The agentId of a previously defined agent | |
* @param target {Body} The Body affected by the agent | |
*/ | |
PhysicsEngine.prototype.attachTo = function attachTo(agentID, target) { | |
_getBoundAgent.call(this, agentID).targets.push(target); | |
}; | |
/** | |
* Undoes PhysicsEngine.attach. Removes an agent and its associated | |
* effect on its affected Bodies. | |
* | |
* @method detach | |
* @param agentID {AgentId} The agentId of a previously defined agent | |
*/ | |
PhysicsEngine.prototype.detach = function detach(id) { | |
// detach from forces/constraints array | |
var agent = this.getAgent(id); | |
var agentArray = _mapAgentArray.call(this, agent); | |
var index = agentArray.indexOf(id); | |
agentArray.splice(index,1); | |
// detach agents array | |
delete this._agents[id]; | |
}; | |
/** | |
* Remove a single Body from a previously defined agent. | |
* | |
* @method detach | |
* @param agentID {AgentId} The agentId of a previously defined agent | |
* @param target {Body} The body to remove from the agent | |
*/ | |
PhysicsEngine.prototype.detachFrom = function detachFrom(id, target) { | |
var boundAgent = _getBoundAgent.call(this, id); | |
if (boundAgent.source === target) this.detach(id); | |
else { | |
var targets = boundAgent.targets; | |
var index = targets.indexOf(target); | |
if (index > -1) targets.splice(index,1); | |
} | |
}; | |
/** | |
* A convenience method to give the Physics Engine a clean slate of | |
* agents. Preserves all added Body objects. | |
* | |
* @method detachAll | |
*/ | |
PhysicsEngine.prototype.detachAll = function detachAll() { | |
this._agents = {}; | |
this._forces = []; | |
this._constraints = []; | |
this._currAgentId = 0; | |
}; | |
function _getBoundAgent(id) { | |
return this._agents[id]; | |
} | |
/** | |
* Returns the corresponding agent given its agentId. | |
* | |
* @method getAgent | |
* @param id {AgentId} | |
*/ | |
PhysicsEngine.prototype.getAgent = function getAgent(id) { | |
return _getBoundAgent.call(this, id).agent; | |
}; | |
/** | |
* Returns all particles that are currently managed by the Physics Engine. | |
* | |
* @method getParticles | |
* @return particles {Array.Particles} | |
*/ | |
PhysicsEngine.prototype.getParticles = function getParticles() { | |
return this._particles; | |
}; | |
/** | |
* Returns all bodies, except particles, that are currently managed by the Physics Engine. | |
* | |
* @method getBodies | |
* @return bodies {Array.Bodies} | |
*/ | |
PhysicsEngine.prototype.getBodies = function getBodies() { | |
return this._bodies; | |
}; | |
/** | |
* Returns all bodies that are currently managed by the Physics Engine. | |
* | |
* @method getBodies | |
* @return bodies {Array.Bodies} | |
*/ | |
PhysicsEngine.prototype.getParticlesAndBodies = function getParticlesAndBodies() { | |
return this.getParticles().concat(this.getBodies()); | |
}; | |
/** | |
* Iterates over every Particle and applies a function whose first | |
* argument is the Particle | |
* | |
* @method forEachParticle | |
* @param fn {Function} Function to iterate over | |
* @param [dt] {Number} Delta time | |
*/ | |
PhysicsEngine.prototype.forEachParticle = function forEachParticle(fn, dt) { | |
var particles = this.getParticles(); | |
for (var index = 0, len = particles.length; index < len; index++) | |
fn.call(this, particles[index], dt); | |
}; | |
/** | |
* Iterates over every Body that isn't a Particle and applies | |
* a function whose first argument is the Body | |
* | |
* @method forEachBody | |
* @param fn {Function} Function to iterate over | |
* @param [dt] {Number} Delta time | |
*/ | |
PhysicsEngine.prototype.forEachBody = function forEachBody(fn, dt) { | |
if (!this._hasBodies) return; | |
var bodies = this.getBodies(); | |
for (var index = 0, len = bodies.length; index < len; index++) | |
fn.call(this, bodies[index], dt); | |
}; | |
/** | |
* Iterates over every Body and applies a function whose first | |
* argument is the Body | |
* | |
* @method forEach | |
* @param fn {Function} Function to iterate over | |
* @param [dt] {Number} Delta time | |
*/ | |
PhysicsEngine.prototype.forEach = function forEach(fn, dt) { | |
this.forEachParticle(fn, dt); | |
this.forEachBody(fn, dt); | |
}; | |
function _updateForce(index) { | |
var boundAgent = _getBoundAgent.call(this, this._forces[index]); | |
boundAgent.agent.applyForce(boundAgent.targets, boundAgent.source); | |
} | |
function _updateForces() { | |
for (var index = this._forces.length - 1; index > -1; index--) | |
_updateForce.call(this, index); | |
} | |
function _updateConstraint(index, dt) { | |
var boundAgent = this._agents[this._constraints[index]]; | |
return boundAgent.agent.applyConstraint(boundAgent.targets, boundAgent.source, dt); | |
} | |
function _updateConstraints(dt) { | |
var iteration = 0; | |
while (iteration < this.options.constraintSteps) { | |
for (var index = this._constraints.length - 1; index > -1; index--) | |
_updateConstraint.call(this, index, dt); | |
iteration++; | |
} | |
} | |
function _updateVelocities(particle, dt) { | |
particle.integrateVelocity(dt); | |
} | |
function _updateAngularVelocities(body, dt) { | |
body.integrateAngularMomentum(dt); | |
body.updateAngularVelocity(); | |
} | |
function _updateOrientations(body, dt) { | |
body.integrateOrientation(dt); | |
} | |
function _updatePositions(particle, dt) { | |
particle.integratePosition(dt); | |
particle.emit('update', particle); | |
} | |
function _integrate(dt) { | |
_updateForces.call(this, dt); | |
this.forEach(_updateVelocities, dt); | |
this.forEachBody(_updateAngularVelocities, dt); | |
_updateConstraints.call(this, dt); | |
this.forEachBody(_updateOrientations, dt); | |
this.forEach(_updatePositions, dt); | |
} | |
function _getEnergyParticles() { | |
var energy = 0.0; | |
var particleEnergy = 0.0; | |
this.forEach(function(particle) { | |
particleEnergy = particle.getEnergy(); | |
energy += particleEnergy; | |
if (particleEnergy < particle.sleepTolerance) particle.sleep(); | |
}); | |
return energy; | |
} | |
function _getEnergyForces() { | |
var energy = 0; | |
for (var index = this._forces.length - 1; index > -1; index--) | |
energy += this._forces[index].getEnergy() || 0.0; | |
return energy; | |
} | |
function _getEnergyConstraints() { | |
var energy = 0; | |
for (var index = this._constraints.length - 1; index > -1; index--) | |
energy += this._constraints[index].getEnergy() || 0.0; | |
return energy; | |
} | |
/** | |
* Calculates the kinetic energy of all Body objects and potential energy | |
* of all attached agents. | |
* | |
* TODO: implement. | |
* @method getEnergy | |
* @return energy {Number} | |
*/ | |
PhysicsEngine.prototype.getEnergy = function getEnergy() { | |
return _getEnergyParticles.call(this) + _getEnergyForces.call(this) + _getEnergyConstraints.call(this); | |
}; | |
/** | |
* Updates all Body objects managed by the physics engine over the | |
* time duration since the last time step was called. | |
* | |
* @method step | |
*/ | |
PhysicsEngine.prototype.step = function step() { | |
// if (this.getEnergy() < this.options.sleepTolerance) { | |
// this.sleep(); | |
// return; | |
// }; | |
//set current frame's time | |
var currTime = now(); | |
//milliseconds elapsed since last frame | |
var dtFrame = currTime - this._prevTime; | |
this._prevTime = currTime; | |
if (dtFrame < MIN_TIME_STEP) return; | |
if (dtFrame > MAX_TIME_STEP) dtFrame = MAX_TIME_STEP; | |
//robust integration | |
// this._buffer += dtFrame; | |
// while (this._buffer > this._timestep){ | |
// _integrate.call(this, this._timestep); | |
// this._buffer -= this._timestep; | |
// }; | |
// _integrate.call(this, this._buffer); | |
// this._buffer = 0.0; | |
_integrate.call(this, TIMESTEP); | |
// this.emit('update', this); | |
}; | |
/** | |
* Tells whether the Physics Engine is sleeping or awake. | |
* @method isSleeping | |
* @return {Boolean} | |
*/ | |
PhysicsEngine.prototype.isSleeping = function isSleeping() { | |
return this._isSleeping; | |
}; | |
/** | |
* Stops the Physics Engine from updating. Emits an 'end' event. | |
* @method sleep | |
*/ | |
PhysicsEngine.prototype.sleep = function sleep() { | |
this.emit('end', this); | |
this._isSleeping = true; | |
}; | |
/** | |
* Starts the Physics Engine from updating. Emits an 'start' event. | |
* @method wake | |
*/ | |
PhysicsEngine.prototype.wake = function wake() { | |
this._prevTime = now(); | |
this.emit('start', this); | |
this._isSleeping = false; | |
}; | |
PhysicsEngine.prototype.emit = function emit(type, data) { | |
if (this._eventHandler === null) return; | |
this._eventHandler.emit(type, data); | |
}; | |
PhysicsEngine.prototype.on = function on(event, fn) { | |
if (this._eventHandler === null) this._eventHandler = new EventHandler(); | |
this._eventHandler.on(event, fn); | |
}; | |
module.exports = PhysicsEngine; | |
}); | |
define('famous/inputs/Accumulator',['require','exports','module','famous/core/EventHandler','famous/transitions/Transitionable'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
/** | |
* Accumulates differentials of event sources that emit a `delta` | |
* attribute taking a Number or Array of Number types. The accumulated | |
* value is stored in a getter/setter. | |
* | |
* @class Accumulator | |
* @constructor | |
* @param value {Number|Array|Transitionable} Initializing value | |
* @param [eventName='update'] {String} Name of update event | |
*/ | |
function Accumulator(value, eventName) { | |
if (eventName === undefined) eventName = 'update'; | |
this._state = (value && value.get && value.set) | |
? value | |
: new Transitionable(value || 0); | |
this._eventInput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
this._eventInput.on(eventName, _handleUpdate.bind(this)); | |
} | |
function _handleUpdate(data) { | |
var delta = data.delta; | |
var state = this.get(); | |
if (delta.constructor === state.constructor){ | |
var newState = (delta instanceof Array) | |
? [state[0] + delta[0], state[1] + delta[1]] | |
: state + delta; | |
this.set(newState); | |
} | |
} | |
/** | |
* Basic getter | |
* | |
* @method get | |
* @return {Number|Array} current value | |
*/ | |
Accumulator.prototype.get = function get() { | |
return this._state.get(); | |
}; | |
/** | |
* Basic setter | |
* | |
* @method set | |
* @param value {Number|Array} new value | |
*/ | |
Accumulator.prototype.set = function set(value) { | |
this._state.set(value); | |
}; | |
module.exports = Accumulator; | |
}); | |
define('famous/inputs/DesktopEmulationMode',['require','exports','module'],function(require, exports, module) { | |
var hasTouch = 'ontouchstart' in window; | |
function kill(type) { | |
window.addEventListener(type, function(event) { | |
event.stopPropagation(); | |
return false; | |
}, true); | |
} | |
if (hasTouch) { | |
kill('mousedown'); | |
kill('mousemove'); | |
kill('mouseup'); | |
kill('mouseleave'); | |
} | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/FastClick',['require','exports','module'],function(require, exports, module) { | |
/** | |
* FastClick is an override shim which maps event pairs of | |
* 'touchstart' and 'touchend' which differ by less than a certain | |
* threshold to the 'click' event. | |
* This is used to speed up clicks on some browsers. | |
*/ | |
if (!window.CustomEvent) return; | |
var clickThreshold = 300; | |
var clickWindow = 500; | |
var potentialClicks = {}; | |
var recentlyDispatched = {}; | |
var _now = Date.now; | |
window.addEventListener('touchstart', function(event) { | |
var timestamp = _now(); | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
potentialClicks[touch.identifier] = timestamp; | |
} | |
}); | |
window.addEventListener('touchmove', function(event) { | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
delete potentialClicks[touch.identifier]; | |
} | |
}); | |
window.addEventListener('touchend', function(event) { | |
var currTime = _now(); | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
var startTime = potentialClicks[touch.identifier]; | |
if (startTime && currTime - startTime < clickThreshold) { | |
var clickEvt = new window.CustomEvent('click', { | |
'bubbles': true, | |
'details': touch | |
}); | |
recentlyDispatched[currTime] = event; | |
event.target.dispatchEvent(clickEvt); | |
} | |
delete potentialClicks[touch.identifier]; | |
} | |
}); | |
window.addEventListener('click', function(event) { | |
var currTime = _now(); | |
for (var i in recentlyDispatched) { | |
var previousEvent = recentlyDispatched[i]; | |
if (currTime - i < clickWindow) { | |
if (event instanceof window.MouseEvent && event.target === previousEvent.target) event.stopPropagation(); | |
} | |
else delete recentlyDispatched[i]; | |
} | |
}, true); | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/TwoFingerSync',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Helper to PinchSync, RotateSync, and ScaleSync. Generalized handling of | |
* two-finger touch events. | |
* This class is meant to be overridden and not used directly. | |
* | |
* @class TwoFingerSync | |
* @constructor | |
*/ | |
function TwoFingerSync() { | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this.touchAEnabled = false; | |
this.touchAId = 0; | |
this.posA = null; | |
this.timestampA = 0; | |
this.touchBEnabled = false; | |
this.touchBId = 0; | |
this.posB = null; | |
this.timestampB = 0; | |
this._eventInput.on('touchstart', this.handleStart.bind(this)); | |
this._eventInput.on('touchmove', this.handleMove.bind(this)); | |
this._eventInput.on('touchend', this.handleEnd.bind(this)); | |
this._eventInput.on('touchcancel', this.handleEnd.bind(this)); | |
} | |
TwoFingerSync.calculateAngle = function(posA, posB) { | |
var diffX = posB[0] - posA[0]; | |
var diffY = posB[1] - posA[1]; | |
return Math.atan2(diffY, diffX); | |
}; | |
TwoFingerSync.calculateDistance = function(posA, posB) { | |
var diffX = posB[0] - posA[0]; | |
var diffY = posB[1] - posA[1]; | |
return Math.sqrt(diffX * diffX + diffY * diffY); | |
}; | |
TwoFingerSync.calculateCenter = function(posA, posB) { | |
return [(posA[0] + posB[0]) / 2.0, (posA[1] + posB[1]) / 2.0]; | |
}; | |
var _now = Date.now; | |
// private | |
TwoFingerSync.prototype.handleStart = function handleStart(event) { | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
if (!this.touchAEnabled) { | |
this.touchAId = touch.identifier; | |
this.touchAEnabled = true; | |
this.posA = [touch.pageX, touch.pageY]; | |
this.timestampA = _now(); | |
} | |
else if (!this.touchBEnabled) { | |
this.touchBId = touch.identifier; | |
this.touchBEnabled = true; | |
this.posB = [touch.pageX, touch.pageY]; | |
this.timestampB = _now(); | |
this._startUpdate(event); | |
} | |
} | |
}; | |
// private | |
TwoFingerSync.prototype.handleMove = function handleMove(event) { | |
if (!(this.touchAEnabled && this.touchBEnabled)) return; | |
var prevTimeA = this.timestampA; | |
var prevTimeB = this.timestampB; | |
var diffTime; | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
if (touch.identifier === this.touchAId) { | |
this.posA = [touch.pageX, touch.pageY]; | |
this.timestampA = _now(); | |
diffTime = this.timestampA - prevTimeA; | |
} | |
else if (touch.identifier === this.touchBId) { | |
this.posB = [touch.pageX, touch.pageY]; | |
this.timestampB = _now(); | |
diffTime = this.timestampB - prevTimeB; | |
} | |
} | |
if (diffTime) this._moveUpdate(diffTime); | |
}; | |
// private | |
TwoFingerSync.prototype.handleEnd = function handleEnd(event) { | |
for (var i = 0; i < event.changedTouches.length; i++) { | |
var touch = event.changedTouches[i]; | |
if (touch.identifier === this.touchAId || touch.identifier === this.touchBId) { | |
if (this.touchAEnabled && this.touchBEnabled) { | |
this._eventOutput.emit('end', { | |
touches : [this.touchAId, this.touchBId], | |
angle : this._angle | |
}); | |
} | |
this.touchAEnabled = false; | |
this.touchAId = 0; | |
this.touchBEnabled = false; | |
this.touchBId = 0; | |
} | |
} | |
}; | |
module.exports = TwoFingerSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/PinchSync',['require','exports','module','./TwoFingerSync'],function(require, exports, module) { | |
var TwoFingerSync = require('./TwoFingerSync'); | |
/** | |
* Handles piped in two-finger touch events to change position via pinching / expanding. | |
* Emits 'start', 'update' and 'end' events with | |
* position, velocity, touch ids, and distance between fingers. | |
* | |
* @class PinchSync | |
* @extends TwoFingerSync | |
* @constructor | |
* @param {Object} options default options overrides | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
function PinchSync(options) { | |
TwoFingerSync.call(this); | |
this.options = Object.create(PinchSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._displacement = 0; | |
this._previousDistance = 0; | |
} | |
PinchSync.prototype = Object.create(TwoFingerSync.prototype); | |
PinchSync.prototype.constructor = PinchSync; | |
PinchSync.DEFAULT_OPTIONS = { | |
scale : 1 | |
}; | |
PinchSync.prototype._startUpdate = function _startUpdate(event) { | |
this._previousDistance = TwoFingerSync.calculateDistance(this.posA, this.posB); | |
this._displacement = 0; | |
this._eventOutput.emit('start', { | |
count: event.touches.length, | |
touches: [this.touchAId, this.touchBId], | |
distance: this._dist, | |
center: TwoFingerSync.calculateCenter(this.posA, this.posB) | |
}); | |
}; | |
PinchSync.prototype._moveUpdate = function _moveUpdate(diffTime) { | |
var currDist = TwoFingerSync.calculateDistance(this.posA, this.posB); | |
var center = TwoFingerSync.calculateCenter(this.posA, this.posB); | |
var scale = this.options.scale; | |
var delta = scale * (currDist - this._previousDistance); | |
var velocity = delta / diffTime; | |
this._previousDistance = currDist; | |
this._displacement += delta; | |
this._eventOutput.emit('update', { | |
delta : delta, | |
velocity: velocity, | |
distance: currDist, | |
displacement: this._displacement, | |
center: center, | |
touches: [this.touchAId, this.touchBId] | |
}); | |
}; | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
PinchSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
PinchSync.prototype.setOptions = function setOptions(options) { | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
}; | |
module.exports = PinchSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/RotateSync',['require','exports','module','./TwoFingerSync'],function(require, exports, module) { | |
var TwoFingerSync = require('./TwoFingerSync'); | |
/** | |
* Handles piped in two-finger touch events to increase or decrease scale via pinching / expanding. | |
* Emits 'start', 'update' and 'end' events an object with position, velocity, touch ids, and angle. | |
* Useful for determining a rotation factor from initial two-finger touch. | |
* | |
* @class RotateSync | |
* @extends TwoFingerSync | |
* @constructor | |
* @param {Object} options default options overrides | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
function RotateSync(options) { | |
TwoFingerSync.call(this); | |
this.options = Object.create(RotateSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._angle = 0; | |
this._previousAngle = 0; | |
} | |
RotateSync.prototype = Object.create(TwoFingerSync.prototype); | |
RotateSync.prototype.constructor = RotateSync; | |
RotateSync.DEFAULT_OPTIONS = { | |
scale : 1 | |
}; | |
RotateSync.prototype._startUpdate = function _startUpdate(event) { | |
this._angle = 0; | |
this._previousAngle = TwoFingerSync.calculateAngle(this.posA, this.posB); | |
var center = TwoFingerSync.calculateCenter(this.posA, this.posB); | |
this._eventOutput.emit('start', { | |
count: event.touches.length, | |
angle: this._angle, | |
center: center, | |
touches: [this.touchAId, this.touchBId] | |
}); | |
}; | |
RotateSync.prototype._moveUpdate = function _moveUpdate(diffTime) { | |
var scale = this.options.scale; | |
var currAngle = TwoFingerSync.calculateAngle(this.posA, this.posB); | |
var center = TwoFingerSync.calculateCenter(this.posA, this.posB); | |
var diffTheta = scale * (currAngle - this._previousAngle); | |
var velTheta = diffTheta / diffTime; | |
this._angle += diffTheta; | |
this._eventOutput.emit('update', { | |
delta : diffTheta, | |
velocity: velTheta, | |
angle: this._angle, | |
center: center, | |
touches: [this.touchAId, this.touchBId] | |
}); | |
this._previousAngle = currAngle; | |
}; | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
RotateSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
RotateSync.prototype.setOptions = function setOptions(options) { | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
}; | |
module.exports = RotateSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/ScaleSync',['require','exports','module','./TwoFingerSync'],function(require, exports, module) { | |
var TwoFingerSync = require('./TwoFingerSync'); | |
/** | |
* Handles piped in two-finger touch events to increase or decrease scale via pinching / expanding. | |
* Emits 'start', 'update' and 'end' events an object with position, velocity, touch ids, distance, and scale factor. | |
* Useful for determining a scaling factor from initial two-finger touch. | |
* | |
* @class ScaleSync | |
* @extends TwoFingerSync | |
* @constructor | |
* @param {Object} options default options overrides | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
function ScaleSync(options) { | |
TwoFingerSync.call(this); | |
this.options = Object.create(ScaleSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._scaleFactor = 1; | |
this._startDist = 0; | |
this._eventInput.on('pipe', _reset.bind(this)); | |
} | |
ScaleSync.prototype = Object.create(TwoFingerSync.prototype); | |
ScaleSync.prototype.constructor = ScaleSync; | |
ScaleSync.DEFAULT_OPTIONS = { | |
scale : 1 | |
}; | |
function _reset() { | |
this.touchAId = undefined; | |
this.touchBId = undefined; | |
} | |
// handles initial touch of two fingers | |
ScaleSync.prototype._startUpdate = function _startUpdate(event) { | |
this._scaleFactor = 1; | |
this._startDist = TwoFingerSync.calculateDistance(this.posA, this.posB); | |
this._eventOutput.emit('start', { | |
count: event.touches.length, | |
touches: [this.touchAId, this.touchBId], | |
distance: this._startDist, | |
center: TwoFingerSync.calculateCenter(this.posA, this.posB) | |
}); | |
}; | |
// handles movement of two fingers | |
ScaleSync.prototype._moveUpdate = function _moveUpdate(diffTime) { | |
var scale = this.options.scale; | |
var currDist = TwoFingerSync.calculateDistance(this.posA, this.posB); | |
var center = TwoFingerSync.calculateCenter(this.posA, this.posB); | |
var delta = (currDist - this._startDist) / this._startDist; | |
var newScaleFactor = Math.max(1 + scale * delta, 0); | |
var veloScale = (newScaleFactor - this._scaleFactor) / diffTime; | |
this._eventOutput.emit('update', { | |
delta : delta, | |
scale: newScaleFactor, | |
velocity: veloScale, | |
distance: currDist, | |
center : center, | |
touches: [this.touchAId, this.touchBId] | |
}); | |
this._scaleFactor = newScaleFactor; | |
}; | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
ScaleSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options | |
* @param {Number} [options.scale] scale velocity by this factor | |
*/ | |
ScaleSync.prototype.setOptions = function setOptions(options) { | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
}; | |
module.exports = ScaleSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/inputs/ScrollSync',['require','exports','module','famous/core/EventHandler','famous/core/Engine'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
var Engine = require('famous/core/Engine'); | |
/** | |
* Handles piped in mousewheel events. | |
* Emits 'start', 'update', and 'end' events with payloads including: | |
* delta: change since last position, | |
* position: accumulated deltas, | |
* velocity: speed of change in pixels per ms, | |
* slip: true (unused). | |
* | |
* Can be used as delegate of GenericSync. | |
* | |
* @class ScrollSync | |
* @constructor | |
* @param {Object} [options] overrides of default options | |
* @param {Number} [options.direction] Pay attention to x changes (ScrollSync.DIRECTION_X), | |
* y changes (ScrollSync.DIRECTION_Y) or both (undefined) | |
* @param {Number} [options.minimumEndSpeed] End speed calculation floors at this number, in pixels per ms | |
* @param {boolean} [options.rails] whether to snap position calculations to nearest axis | |
* @param {Number | Array.Number} [options.scale] scale outputs in by scalar or pair of scalars | |
* @param {Number} [options.stallTime] reset time for velocity calculation in ms | |
*/ | |
function ScrollSync(options) { | |
this.options = Object.create(ScrollSync.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this._payload = { | |
delta : null, | |
position : null, | |
velocity : null, | |
slip : true | |
}; | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this._position = (this.options.direction === undefined) ? [0,0] : 0; | |
this._prevTime = undefined; | |
this._prevVel = undefined; | |
this._eventInput.on('mousewheel', _handleMove.bind(this)); | |
this._eventInput.on('wheel', _handleMove.bind(this)); | |
this._inProgress = false; | |
this._loopBound = false; | |
} | |
ScrollSync.DEFAULT_OPTIONS = { | |
direction: undefined, | |
minimumEndSpeed: Infinity, | |
rails: false, | |
scale: 1, | |
stallTime: 50, | |
lineHeight: 40 | |
}; | |
ScrollSync.DIRECTION_X = 0; | |
ScrollSync.DIRECTION_Y = 1; | |
var MINIMUM_TICK_TIME = 8; | |
var _now = Date.now; | |
function _newFrame() { | |
if (this._inProgress && (_now() - this._prevTime) > this.options.stallTime) { | |
this._position = (this.options.direction === undefined) ? [0,0] : 0; | |
this._inProgress = false; | |
var finalVel = (Math.abs(this._prevVel) >= this.options.minimumEndSpeed) | |
? this._prevVel | |
: 0; | |
var payload = this._payload; | |
payload.position = this._position; | |
payload.velocity = finalVel; | |
payload.slip = true; | |
this._eventOutput.emit('end', payload); | |
} | |
} | |
function _handleMove(event) { | |
event.preventDefault(); | |
if (!this._inProgress) { | |
this._inProgress = true; | |
payload = this._payload; | |
payload.slip = true; | |
payload.position = this._position; | |
payload.clientX = event.clientX; | |
payload.clientY = event.clientY; | |
payload.offsetX = event.offsetX; | |
payload.offsetY = event.offsetY; | |
this._eventOutput.emit('start', payload); | |
if (!this._loopBound) { | |
Engine.on('prerender', _newFrame.bind(this)); | |
this._loopBound = true; | |
} | |
} | |
var currTime = _now(); | |
var prevTime = this._prevTime || currTime; | |
var diffX = (event.wheelDeltaX !== undefined) ? event.wheelDeltaX : -event.deltaX; | |
var diffY = (event.wheelDeltaY !== undefined) ? event.wheelDeltaY : -event.deltaY; | |
if (event.deltaMode === 1) { // units in lines, not pixels | |
diffX *= this.options.lineHeight; | |
diffY *= this.options.lineHeight; | |
} | |
if (this.options.rails) { | |
if (Math.abs(diffX) > Math.abs(diffY)) diffY = 0; | |
else diffX = 0; | |
} | |
var diffTime = Math.max(currTime - prevTime, MINIMUM_TICK_TIME); // minimum tick time | |
var velX = diffX / diffTime; | |
var velY = diffY / diffTime; | |
var scale = this.options.scale; | |
var nextVel; | |
var nextDelta; | |
if (this.options.direction === ScrollSync.DIRECTION_X) { | |
nextDelta = scale * diffX; | |
nextVel = scale * velX; | |
this._position += nextDelta; | |
} | |
else if (this.options.direction === ScrollSync.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.slip = true; | |
this._eventOutput.emit('update', payload); | |
this._prevTime = currTime; | |
this._prevVel = nextVel; | |
} | |
/** | |
* Return entire options dictionary, including defaults. | |
* | |
* @method getOptions | |
* @return {Object} configuration options | |
*/ | |
ScrollSync.prototype.getOptions = function getOptions() { | |
return this.options; | |
}; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options | |
* @param {Number} [options.minimimEndSpeed] If final velocity smaller than this, round down to 0. | |
* @param {Number} [options.stallTime] ms of non-motion before 'end' emitted | |
* @param {Number} [options.rails] whether to constrain to nearest axis. | |
* @param {Number} [options.direction] ScrollSync.DIRECTION_X, DIRECTION_Y - | |
* pay attention to one specific direction. | |
* @param {Number} [options.scale] constant factor to scale velocity output | |
*/ | |
ScrollSync.prototype.setOptions = function setOptions(options) { | |
if (options.direction !== undefined) this.options.direction = options.direction; | |
if (options.minimumEndSpeed !== undefined) this.options.minimumEndSpeed = options.minimumEndSpeed; | |
if (options.rails !== undefined) this.options.rails = options.rails; | |
if (options.scale !== undefined) this.options.scale = options.scale; | |
if (options.stallTime !== undefined) this.options.stallTime = options.stallTime; | |
}; | |
module.exports = ScrollSync; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/CachedMap',['require','exports','module'],function(require, exports, module) { | |
/** | |
* A simple in-memory object cache. Used as a helper for Views with | |
* provider functions. | |
* @class CachedMap | |
* @constructor | |
*/ | |
function CachedMap(mappingFunction) { | |
this._map = mappingFunction || null; | |
this._cachedOutput = null; | |
this._cachedInput = Number.NaN; //never valid as input | |
} | |
/** | |
* Creates a mapping function with a cache. | |
* This is the main entrypoint for this object. | |
* @static | |
* @method create | |
* @param {function} mappingFunction mapping | |
* @return {function} memoized mapping function | |
*/ | |
CachedMap.create = function create(mappingFunction) { | |
var instance = new CachedMap(mappingFunction); | |
return instance.get.bind(instance); | |
}; | |
/** | |
* Retrieve items from cache or from mapping functin. | |
* | |
* @method get | |
* @param {Object} input input key | |
*/ | |
CachedMap.prototype.get = function get(input) { | |
if (input !== this._cachedInput) { | |
this._cachedInput = input; | |
this._cachedOutput = this._map(input); | |
} | |
return this._cachedOutput; | |
}; | |
module.exports = CachedMap; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/Easing',['require','exports','module'],function(require, exports, module) { | |
/* | |
* 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; | |
} | |
}; | |
module.exports = 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/integrators/SymplecticEuler',['require','exports','module','famous/core/OptionsManager'],function(require, exports, module) { | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* Ordinary Differential Equation (ODE) Integrator. | |
* Manages updating a physics body's state over time. | |
* | |
* p = position, v = velocity, m = mass, f = force, dt = change in time | |
* | |
* v <- v + dt * f / m | |
* p <- p + dt * v | |
* | |
* q = orientation, w = angular velocity, L = angular momentum | |
* | |
* L <- L + dt * t | |
* q <- q + dt/2 * q * w | |
* | |
* @class SymplecticEuler | |
* @constructor | |
* @param {Object} options Options to set | |
*/ | |
function SymplecticEuler(options) { | |
this.options = Object.create(SymplecticEuler.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
} | |
/** | |
* @property SymplecticEuler.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
SymplecticEuler.DEFAULT_OPTIONS = { | |
/** | |
* The maximum velocity of a physics body | |
* Range : [0, Infinity] | |
* @attribute velocityCap | |
* @type Number | |
*/ | |
velocityCap : undefined, | |
/** | |
* The maximum angular velocity of a physics body | |
* Range : [0, Infinity] | |
* @attribute angularVelocityCap | |
* @type Number | |
*/ | |
angularVelocityCap : undefined | |
}; | |
/* | |
* Setter for options | |
* | |
* @method setOptions | |
* @param {Object} options | |
*/ | |
SymplecticEuler.prototype.setOptions = function setOptions(options) { | |
this._optionsManager.patch(options); | |
}; | |
/* | |
* Getter for options | |
* | |
* @method getOptions | |
* @return {Object} options | |
*/ | |
SymplecticEuler.prototype.getOptions = function getOptions() { | |
return this._optionsManager.value(); | |
}; | |
/* | |
* Updates the velocity of a physics body from its accumulated force. | |
* v <- v + dt * f / m | |
* | |
* @method integrateVelocity | |
* @param {Body} physics body | |
* @param {Number} dt delta time | |
*/ | |
SymplecticEuler.prototype.integrateVelocity = function integrateVelocity(body, dt) { | |
var v = body.velocity; | |
var w = body.inverseMass; | |
var f = body.force; | |
if (f.isZero()) return; | |
v.add(f.mult(dt * w)).put(v); | |
f.clear(); | |
}; | |
/* | |
* Updates the position of a physics body from its velocity. | |
* p <- p + dt * v | |
* | |
* @method integratePosition | |
* @param {Body} physics body | |
* @param {Number} dt delta time | |
*/ | |
SymplecticEuler.prototype.integratePosition = function integratePosition(body, dt) { | |
var p = body.position; | |
var v = body.velocity; | |
if (this.options.velocityCap) v.cap(this.options.velocityCap).put(v); | |
p.add(v.mult(dt)).put(p); | |
}; | |
/* | |
* Updates the angular momentum of a physics body from its accumuled torque. | |
* L <- L + dt * t | |
* | |
* @method integrateAngularMomentum | |
* @param {Body} physics body (except a particle) | |
* @param {Number} dt delta time | |
*/ | |
SymplecticEuler.prototype.integrateAngularMomentum = function integrateAngularMomentum(body, dt) { | |
var L = body.angularMomentum; | |
var t = body.torque; | |
if (t.isZero()) return; | |
if (this.options.angularVelocityCap) t.cap(this.options.angularVelocityCap).put(t); | |
L.add(t.mult(dt)).put(L); | |
t.clear(); | |
}; | |
/* | |
* Updates the orientation of a physics body from its angular velocity. | |
* q <- q + dt/2 * q * w | |
* | |
* @method integrateOrientation | |
* @param {Body} physics body (except a particle) | |
* @param {Number} dt delta time | |
*/ | |
SymplecticEuler.prototype.integrateOrientation = function integrateOrientation(body, dt) { | |
var q = body.orientation; | |
var w = body.angularVelocity; | |
if (w.isZero()) return; | |
q.add(q.multiply(w).scalarMultiply(0.5 * dt)).put(q); | |
// q.normalize.put(q); | |
}; | |
module.exports = SymplecticEuler; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/bodies/Particle',['require','exports','module','famous/math/Vector','famous/core/Transform','famous/core/EventHandler','../integrators/SymplecticEuler'],function(require, exports, module) { | |
var Vector = require('famous/math/Vector'); | |
var Transform = require('famous/core/Transform'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Integrator = require('../integrators/SymplecticEuler'); | |
/** | |
* A point body that is controlled by the Physics Engine. A particle has | |
* position and velocity states that are updated by the Physics Engine. | |
* Ultimately, a particle is a _special type of modifier, and can be added to | |
* the Famous render tree like any other modifier. | |
* | |
* @constructor | |
* @class Particle | |
* @uses EventHandler | |
* @uses Modifier | |
* @extensionfor Body | |
* @param {Options} [options] An object of configurable options. | |
* @param {Array} [options.position] The position of the particle. | |
* @param {Array} [options.velocity] The velocity of the particle. | |
* @param {Number} [options.mass] The mass of the particle. | |
* @param {Hexadecimal} [options.axis] The axis a particle can move along. Can be bitwise ORed e.g., Particle.AXES.X, Particle.AXES.X | Particle.AXES.Y | |
* | |
*/ | |
function Particle(options) { | |
options = options || {}; | |
// registers | |
this.position = new Vector(); | |
this.velocity = new Vector(); | |
this.force = new Vector(); | |
var defaults = Particle.DEFAULT_OPTIONS; | |
// set vectors | |
this.setPosition(options.position || defaults.position); | |
this.setVelocity(options.velocity || defaults.velocity); | |
this.force.set(options.force || [0,0,0]); | |
// set scalars | |
this.mass = (options.mass !== undefined) | |
? options.mass | |
: defaults.mass; | |
this.axis = (options.axis !== undefined) | |
? options.axis | |
: defaults.axis; | |
this.inverseMass = 1 / this.mass; | |
// state variables | |
this._isSleeping = false; | |
this._engine = null; | |
this._eventOutput = null; | |
this._positionGetter = null; | |
this.transform = Transform.identity.slice(); | |
// cached _spec | |
this._spec = { | |
transform : this.transform, | |
target : null | |
}; | |
} | |
Particle.DEFAULT_OPTIONS = { | |
position : [0,0,0], | |
velocity : [0,0,0], | |
mass : 1, | |
axis : undefined | |
}; | |
/** | |
* Kinetic energy threshold needed to update the body | |
* | |
* @property SLEEP_TOLERANCE | |
* @type Number | |
* @static | |
* @default 1e-7 | |
*/ | |
Particle.SLEEP_TOLERANCE = 1e-7; | |
/** | |
* Axes by which a body can translate | |
* | |
* @property AXES | |
* @type Hexadecimal | |
* @static | |
* @default 1e-7 | |
*/ | |
Particle.AXES = { | |
X : 0x00, // hexadecimal for 0 | |
Y : 0x01, // hexadecimal for 1 | |
Z : 0x02 // hexadecimal for 2 | |
}; | |
// Integrator for updating the particle's state | |
// TODO: make this a singleton | |
Particle.INTEGRATOR = new Integrator(); | |
//Catalogue of outputted events | |
var _events = { | |
start : 'start', | |
update : 'update', | |
end : 'end' | |
}; | |
// Cached timing function | |
var now = (function() { | |
return Date.now; | |
})(); | |
/** | |
* Stops the particle from updating | |
* @method sleep | |
*/ | |
Particle.prototype.sleep = function sleep() { | |
if (this._isSleeping) return; | |
this.emit(_events.end, this); | |
this._isSleeping = true; | |
}; | |
/** | |
* Starts the particle update | |
* @method wake | |
*/ | |
Particle.prototype.wake = function wake() { | |
if (!this._isSleeping) return; | |
this.emit(_events.start, this); | |
this._isSleeping = false; | |
this._prevTime = now(); | |
}; | |
/** | |
* @attribute isBody | |
* @type Boolean | |
* @static | |
*/ | |
Particle.prototype.isBody = false; | |
/** | |
* Basic setter for position | |
* @method getPosition | |
* @param position {Array|Vector} | |
*/ | |
Particle.prototype.setPosition = function setPosition(position) { | |
this.position.set(position); | |
}; | |
/** | |
* 1-dimensional setter for position | |
* @method setPosition1D | |
* @param value {Number} | |
*/ | |
Particle.prototype.setPosition1D = function setPosition1D(x) { | |
this.position.x = x; | |
}; | |
/** | |
* Basic getter function for position | |
* @method getPosition | |
* @return position {Array} | |
*/ | |
Particle.prototype.getPosition = function getPosition() { | |
if (this._positionGetter instanceof Function) | |
this.setPosition(this._positionGetter()); | |
this._engine.step(); | |
return this.position.get(); | |
}; | |
/** | |
* 1-dimensional getter for position | |
* @method getPosition1D | |
* @return value {Number} | |
*/ | |
Particle.prototype.getPosition1D = function getPosition1D() { | |
this._engine.step(); | |
return this.position.x; | |
}; | |
/** | |
* Defines the position from outside the Physics Engine | |
* @method positionFrom | |
* @param positionGetter {Function} | |
*/ | |
Particle.prototype.positionFrom = function positionFrom(positionGetter) { | |
this._positionGetter = positionGetter; | |
}; | |
/** | |
* Basic setter function for velocity Vector | |
* @method setVelocity | |
* @function | |
*/ | |
Particle.prototype.setVelocity = function setVelocity(velocity) { | |
this.velocity.set(velocity); | |
this.wake(); | |
}; | |
/** | |
* 1-dimensional setter for velocity | |
* @method setVelocity1D | |
* @param velocity {Number} | |
*/ | |
Particle.prototype.setVelocity1D = function setVelocity1D(x) { | |
this.velocity.x = x; | |
this.wake(); | |
}; | |
/** | |
* Basic getter function for velocity Vector | |
* @method getVelocity | |
* @return velocity {Array} | |
*/ | |
Particle.prototype.getVelocity = function getVelocity() { | |
return this.velocity.get(); | |
}; | |
/** | |
* 1-dimensional getter for velocity | |
* @method getVelocity1D | |
* @return velocity {Number} | |
*/ | |
Particle.prototype.getVelocity1D = function getVelocity1D() { | |
return this.velocity.x; | |
}; | |
/** | |
* Basic setter function for mass quantity | |
* @method setMass | |
* @param mass {Number} mass | |
*/ | |
Particle.prototype.setMass = function setMass(mass) { | |
this.mass = mass; | |
this.inverseMass = 1 / mass; | |
}; | |
/** | |
* Basic getter function for mass quantity | |
* @method getMass | |
* @return mass {Number} | |
*/ | |
Particle.prototype.getMass = function getMass() { | |
return this.mass; | |
}; | |
/** | |
* Reset position and velocity | |
* @method reset | |
* @param position {Array|Vector} | |
* @param velocity {Array|Vector} | |
*/ | |
Particle.prototype.reset = function reset(position, velocity) { | |
this.setPosition(position || [0,0,0]); | |
this.setVelocity(velocity || [0,0,0]); | |
}; | |
/** | |
* Add force vector to existing internal force Vector | |
* @method applyForce | |
* @param force {Vector} | |
*/ | |
Particle.prototype.applyForce = function applyForce(force) { | |
if (force.isZero()) return; | |
this.force.add(force).put(this.force); | |
this.wake(); | |
}; | |
/** | |
* Add impulse (change in velocity) Vector to this Vector's velocity. | |
* @method applyImpulse | |
* @param impulse {Vector} | |
*/ | |
Particle.prototype.applyImpulse = function applyImpulse(impulse) { | |
if (impulse.isZero()) return; | |
var velocity = this.velocity; | |
velocity.add(impulse.mult(this.inverseMass)).put(velocity); | |
}; | |
/** | |
* Update a particle's velocity from its force accumulator | |
* @method integrateVelocity | |
* @param dt {Number} Time differential | |
*/ | |
Particle.prototype.integrateVelocity = function integrateVelocity(dt) { | |
Particle.INTEGRATOR.integrateVelocity(this, dt); | |
}; | |
/** | |
* Update a particle's position from its velocity | |
* @method integratePosition | |
* @param dt {Number} Time differential | |
*/ | |
Particle.prototype.integratePosition = function integratePosition(dt) { | |
Particle.INTEGRATOR.integratePosition(this, dt); | |
}; | |
/** | |
* Update the position and velocity of the particle | |
* @method _integrate | |
* @protected | |
* @param dt {Number} Time differential | |
*/ | |
Particle.prototype._integrate = function _integrate(dt) { | |
this.integrateVelocity(dt); | |
this.integratePosition(dt); | |
}; | |
/** | |
* Get kinetic energy of the particle. | |
* @method getEnergy | |
* @function | |
*/ | |
Particle.prototype.getEnergy = function getEnergy() { | |
return 0.5 * this.mass * this.velocity.normSquared(); | |
}; | |
/** | |
* Generate transform from the current position state | |
* @method getTransform | |
* @return Transform {Transform} | |
*/ | |
Particle.prototype.getTransform = function getTransform() { | |
this._engine.step(); | |
var position = this.position; | |
var axis = this.axis; | |
var transform = this.transform; | |
if (axis !== undefined) { | |
if (axis & ~Particle.AXES.X) { | |
position.x = 0; | |
} | |
if (axis & ~Particle.AXES.Y) { | |
position.y = 0; | |
} | |
if (axis & ~Particle.AXES.Z) { | |
position.z = 0; | |
} | |
} | |
transform[12] = position.x; | |
transform[13] = position.y; | |
transform[14] = position.z; | |
return transform; | |
}; | |
/** | |
* The modify interface of a Modifier | |
* @method modify | |
* @param target {Spec} | |
* @return Spec {Spec} | |
*/ | |
Particle.prototype.modify = function modify(target) { | |
var _spec = this._spec; | |
_spec.transform = this.getTransform(); | |
_spec.target = target; | |
return _spec; | |
}; | |
// private | |
function _createEventOutput() { | |
this._eventOutput = new EventHandler(); | |
this._eventOutput.bindThis(this); | |
//overrides on/removeListener/pipe/unpipe methods | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
Particle.prototype.emit = function emit(type, data) { | |
if (!this._eventOutput) return; | |
this._eventOutput.emit(type, data); | |
}; | |
Particle.prototype.on = function on() { | |
_createEventOutput.call(this); | |
return this.on.apply(this, arguments); | |
}; | |
Particle.prototype.removeListener = function removeListener() { | |
_createEventOutput.call(this); | |
return this.removeListener.apply(this, arguments); | |
}; | |
Particle.prototype.pipe = function pipe() { | |
_createEventOutput.call(this); | |
return this.pipe.apply(this, arguments); | |
}; | |
Particle.prototype.unpipe = function unpipe() { | |
_createEventOutput.call(this); | |
return this.unpipe.apply(this, arguments); | |
}; | |
module.exports = Particle; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Constraint',['require','exports','module','famous/core/EventHandler'],function(require, exports, module) { | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Allows for two circular bodies to collide and bounce off each other. | |
* | |
* @class Constraint | |
* @constructor | |
* @uses EventHandler | |
* @param options {Object} | |
*/ | |
function Constraint() { | |
this.options = this.options || {}; | |
this._energy = 0.0; | |
this._eventOutput = null; | |
} | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Constraint.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds an impulse to a physics body's velocity due to the constraint | |
* | |
* @method applyConstraint | |
*/ | |
Constraint.prototype.applyConstraint = function applyConstraint() {}; | |
/** | |
* Getter for energy | |
* | |
* @method getEnergy | |
* @return energy {Number} | |
*/ | |
Constraint.prototype.getEnergy = function getEnergy() { | |
return this._energy; | |
}; | |
/** | |
* Setter for energy | |
* | |
* @method setEnergy | |
* @param energy {Number} | |
*/ | |
Constraint.prototype.setEnergy = function setEnergy(energy) { | |
this._energy = energy; | |
}; | |
function _createEventOutput() { | |
this._eventOutput = new EventHandler(); | |
this._eventOutput.bindThis(this); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
Constraint.prototype.on = function on() { | |
_createEventOutput.call(this); | |
return this.on.apply(this, arguments); | |
}; | |
Constraint.prototype.addListener = function addListener() { | |
_createEventOutput.call(this); | |
return this.addListener.apply(this, arguments); | |
}; | |
Constraint.prototype.pipe = function pipe() { | |
_createEventOutput.call(this); | |
return this.pipe.apply(this, arguments); | |
}; | |
Constraint.prototype.removeListener = function removeListener() { | |
return this.removeListener.apply(this, arguments); | |
}; | |
Constraint.prototype.unpipe = function unpipe() { | |
return this.unpipe.apply(this, arguments); | |
}; | |
module.exports = Constraint; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Snap',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A spring constraint is like a spring force, except that it is always | |
* numerically stable (even for low periods), at the expense of introducing | |
* damping (even with dampingRatio set to 0). | |
* | |
* Use this if you need fast spring-like behavior, e.g., snapping | |
* | |
* @class Snap | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.period] The amount of time in milliseconds taken for one complete oscillation when there is no damping. Range : [150, Infinity] | |
* @param {Number} [options.dampingRatio] Additional damping of the spring. Range : [0, 1]. At 0 this spring will still be damped, at 1 the spring will be critically damped (the spring will never oscillate) | |
* @param {Number} [options.length] The rest length of the spring. Range: [0, Infinity]. | |
* @param {Array} [options.anchor] The location of the spring's anchor, if not another physics body. | |
* | |
*/ | |
function Snap(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.pDiff = new Vector(); | |
this.vDiff = new Vector(); | |
this.impulse1 = new Vector(); | |
this.impulse2 = new Vector(); | |
Constraint.call(this); | |
} | |
Snap.prototype = Object.create(Constraint.prototype); | |
Snap.prototype.constructor = Snap; | |
Snap.DEFAULT_OPTIONS = { | |
period : 300, | |
dampingRatio : 0.1, | |
length : 0, | |
anchor : undefined | |
}; | |
/** const */ var pi = Math.PI; | |
function _calcEnergy(impulse, disp, dt) { | |
return Math.abs(impulse.dot(disp)/dt); | |
} | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param options {Objects} options | |
*/ | |
Snap.prototype.setOptions = function setOptions(options) { | |
if (options.anchor !== undefined) { | |
if (options.anchor instanceof Vector) this.options.anchor = options.anchor; | |
if (options.anchor.position instanceof Vector) this.options.anchor = options.anchor.position; | |
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | |
} | |
if (options.length !== undefined) this.options.length = options.length; | |
if (options.dampingRatio !== undefined) this.options.dampingRatio = options.dampingRatio; | |
if (options.period !== undefined) this.options.period = options.period; | |
}; | |
/** | |
* Set the anchor position | |
* | |
* @method setOptions | |
* @param {Array} v TODO | |
*/ | |
Snap.prototype.setAnchor = function setAnchor(v) { | |
if (this.options.anchor !== undefined) this.options.anchor = new Vector(); | |
this.options.anchor.set(v); | |
}; | |
/** | |
* Calculates energy of spring | |
* | |
* @method getEnergy | |
* @param {Object} target TODO | |
* @param {Object} source TODO | |
* @return energy {Number} | |
*/ | |
Snap.prototype.getEnergy = function getEnergy(target, source) { | |
var options = this.options; | |
var restLength = options.length; | |
var anchor = options.anchor || source.position; | |
var strength = Math.pow(2 * pi / options.period, 2); | |
var dist = anchor.sub(target.position).norm() - restLength; | |
return 0.5 * strength * dist * dist; | |
}; | |
/** | |
* Adds a spring impulse to a physics body's velocity due to the constraint | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply the constraint to | |
* @param source {Body} The source of the constraint | |
* @param dt {Number} Delta time | |
*/ | |
Snap.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
var options = this.options; | |
var pDiff = this.pDiff; | |
var vDiff = this.vDiff; | |
var impulse1 = this.impulse1; | |
var impulse2 = this.impulse2; | |
var length = options.length; | |
var anchor = options.anchor || source.position; | |
var period = options.period; | |
var dampingRatio = options.dampingRatio; | |
for (var i = 0; i < targets.length ; i++) { | |
var target = targets[i]; | |
var p1 = target.position; | |
var v1 = target.velocity; | |
var m1 = target.mass; | |
var w1 = target.inverseMass; | |
pDiff.set(p1.sub(anchor)); | |
var dist = pDiff.norm() - length; | |
var effMass; | |
if (source) { | |
var w2 = source.inverseMass; | |
var v2 = source.velocity; | |
vDiff.set(v1.sub(v2)); | |
effMass = 1/(w1 + w2); | |
} | |
else { | |
vDiff.set(v1); | |
effMass = m1; | |
} | |
var gamma; | |
var beta; | |
if (this.options.period === 0) { | |
gamma = 0; | |
beta = 1; | |
} | |
else { | |
var k = 4 * effMass * pi * pi / (period * period); | |
var c = 4 * effMass * pi * dampingRatio / period; | |
beta = dt * k / (c + dt * k); | |
gamma = 1 / (c + dt*k); | |
} | |
var antiDrift = beta/dt * dist; | |
pDiff.normalize(-antiDrift) | |
.sub(vDiff) | |
.mult(dt / (gamma + dt/effMass)) | |
.put(impulse1); | |
// var n = new Vector(); | |
// n.set(pDiff.normalize()); | |
// var lambda = -(n.dot(vDiff) + antiDrift) / (gamma + dt/effMass); | |
// impulse2.set(n.mult(dt*lambda)); | |
target.applyImpulse(impulse1); | |
if (source) { | |
impulse1.mult(-1).put(impulse2); | |
source.applyImpulse(impulse2); | |
} | |
this.setEnergy(_calcEnergy(impulse1, pDiff, dt)); | |
} | |
}; | |
module.exports = Snap; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/SnapTransition',['require','exports','module','famous/physics/PhysicsEngine','famous/physics/bodies/Particle','famous/physics/constraints/Snap','famous/math/Vector'],function(require, exports, module) { | |
var PE = require('famous/physics/PhysicsEngine'); | |
var Particle = require('famous/physics/bodies/Particle'); | |
var Spring = require('famous/physics/constraints/Snap'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* SnapTransition is a method of transitioning between two values (numbers, | |
* or arrays of numbers). It is similar to SpringTransition except | |
* the transition can be much faster and always has a damping effect. | |
* | |
* @class SnapTransition | |
* @constructor | |
* | |
* @param [state=0] {Number|Array} Initial state | |
*/ | |
function SnapTransition(state) { | |
state = state || 0; | |
this.endState = new Vector(state); | |
this.initState = new Vector(); | |
this._dimensions = 1; | |
this._restTolerance = 1e-10; | |
this._absRestTolerance = this._restTolerance; | |
this._callback = undefined; | |
this.PE = new PE(); | |
this.particle = new Particle(); | |
this.spring = new Spring({anchor : this.endState}); | |
this.PE.addBody(this.particle); | |
this.PE.attach(this.spring, this.particle); | |
} | |
SnapTransition.SUPPORTS_MULTIPLE = 3; | |
/** | |
* @property SnapTransition.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
SnapTransition.DEFAULT_OPTIONS = { | |
/** | |
* The amount of time in milliseconds taken for one complete oscillation | |
* when there is no damping | |
* Range : [0, Infinity] | |
* | |
* @attribute period | |
* @type Number | |
* @default 100 | |
*/ | |
period : 100, | |
/** | |
* The damping of the snap. | |
* Range : [0, 1] | |
* | |
* @attribute dampingRatio | |
* @type Number | |
* @default 0.2 | |
*/ | |
dampingRatio : 0.2, | |
/** | |
* The initial velocity of the transition. | |
* | |
* @attribute velocity | |
* @type Number|Array | |
* @default 0 | |
*/ | |
velocity : 0 | |
}; | |
function _getEnergy() { | |
return this.particle.getEnergy() + this.spring.getEnergy(this.particle); | |
} | |
function _setAbsoluteRestTolerance() { | |
var distance = this.endState.sub(this.initState).normSquared(); | |
this._absRestTolerance = (distance === 0) | |
? this._restTolerance | |
: this._restTolerance * distance; | |
} | |
function _setTarget(target) { | |
this.endState.set(target); | |
_setAbsoluteRestTolerance.call(this); | |
} | |
function _wake() { | |
this.PE.wake(); | |
} | |
function _sleep() { | |
this.PE.sleep(); | |
} | |
function _setParticlePosition(p) { | |
this.particle.position.set(p); | |
} | |
function _setParticleVelocity(v) { | |
this.particle.velocity.set(v); | |
} | |
function _getParticlePosition() { | |
return (this._dimensions === 0) | |
? this.particle.getPosition1D() | |
: this.particle.getPosition(); | |
} | |
function _getParticleVelocity() { | |
return (this._dimensions === 0) | |
? this.particle.getVelocity1D() | |
: this.particle.getVelocity(); | |
} | |
function _setCallback(callback) { | |
this._callback = callback; | |
} | |
function _setupDefinition(definition) { | |
var defaults = SnapTransition.DEFAULT_OPTIONS; | |
if (definition.period === undefined) definition.period = defaults.period; | |
if (definition.dampingRatio === undefined) definition.dampingRatio = defaults.dampingRatio; | |
if (definition.velocity === undefined) definition.velocity = defaults.velocity; | |
//setup spring | |
this.spring.setOptions({ | |
period : definition.period, | |
dampingRatio : definition.dampingRatio | |
}); | |
//setup particle | |
_setParticleVelocity.call(this, definition.velocity); | |
} | |
function _update() { | |
if (this.PE.isSleeping()) { | |
if (this._callback) { | |
var cb = this._callback; | |
this._callback = undefined; | |
cb(); | |
} | |
return; | |
} | |
if (_getEnergy.call(this) < this._absRestTolerance) { | |
_setParticlePosition.call(this, this.endState); | |
_setParticleVelocity.call(this, [0,0,0]); | |
_sleep.call(this); | |
} | |
} | |
/** | |
* Resets the state and velocity | |
* | |
* @method reset | |
* | |
* @param state {Number|Array} State | |
* @param [velocity] {Number|Array} Velocity | |
*/ | |
SnapTransition.prototype.reset = function reset(state, velocity) { | |
this._dimensions = (state instanceof Array) | |
? state.length | |
: 0; | |
this.initState.set(state); | |
_setParticlePosition.call(this, state); | |
_setTarget.call(this, state); | |
if (velocity) _setParticleVelocity.call(this, velocity); | |
_setCallback.call(this, undefined); | |
}; | |
/** | |
* Getter for velocity | |
* | |
* @method getVelocity | |
* | |
* @return velocity {Number|Array} | |
*/ | |
SnapTransition.prototype.getVelocity = function getVelocity() { | |
return _getParticleVelocity.call(this); | |
}; | |
/** | |
* Setter for velocity | |
* | |
* @method setVelocity | |
* | |
* @return velocity {Number|Array} | |
*/ | |
SnapTransition.prototype.setVelocity = function setVelocity(velocity) { | |
this.call(this, _setParticleVelocity(velocity)); | |
}; | |
/** | |
* Detects whether a transition is in progress | |
* | |
* @method isActive | |
* | |
* @return {Boolean} | |
*/ | |
SnapTransition.prototype.isActive = function isActive() { | |
return !this.PE.isSleeping(); | |
}; | |
/** | |
* Halt the transition | |
* | |
* @method halt | |
*/ | |
SnapTransition.prototype.halt = function halt() { | |
this.set(this.get()); | |
}; | |
/** | |
* Get the current position of the transition | |
s * | |
* @method get | |
* | |
* @return state {Number|Array} | |
*/ | |
SnapTransition.prototype.get = function get() { | |
_update.call(this); | |
return _getParticlePosition.call(this); | |
}; | |
/** | |
* Set the end position and transition, with optional callback on completion. | |
* | |
* @method set | |
* | |
* @param state {Number|Array} Final state | |
* @param [definition] {Object} Transition definition | |
* @param [callback] {Function} Callback | |
*/ | |
SnapTransition.prototype.set = function set(state, definition, callback) { | |
if (!definition) { | |
this.reset(state); | |
if (callback) callback(); | |
return; | |
} | |
this._dimensions = (state instanceof Array) | |
? state.length | |
: 0; | |
_wake.call(this); | |
_setupDefinition.call(this, definition); | |
_setTarget.call(this, state); | |
_setCallback.call(this, callback); | |
}; | |
module.exports = SnapTransition; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/forces/Force',['require','exports','module','famous/math/Vector','famous/core/EventHandler'],function(require, exports, module) { | |
var Vector = require('famous/math/Vector'); | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Force base class. | |
* | |
* @class Force | |
* @uses EventHandler | |
* @constructor | |
*/ | |
function Force(force) { | |
this.force = new Vector(force); | |
this._energy = 0.0; | |
this._eventOutput = null; | |
} | |
/** | |
* Basic setter for options | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Force.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds a force to a physics body's force accumulator. | |
* | |
* @method applyForce | |
* @param body {Body} | |
*/ | |
Force.prototype.applyForce = function applyForce(body) { | |
body.applyForce(this.force); | |
}; | |
/** | |
* Getter for a force's potential energy. | |
* | |
* @method getEnergy | |
* @return energy {Number} | |
*/ | |
Force.prototype.getEnergy = function getEnergy() { | |
return this._energy; | |
}; | |
/* | |
* Setter for a force's potential energy. | |
* | |
* @method setEnergy | |
* @param energy {Number} | |
*/ | |
Force.prototype.setEnergy = function setEnergy(energy) { | |
this._energy = energy; | |
}; | |
function _createEventOutput() { | |
this._eventOutput = new EventHandler(); | |
this._eventOutput.bindThis(this); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
Force.prototype.on = function on() { | |
_createEventOutput.call(this); | |
return this.on.apply(this, arguments); | |
}; | |
Force.prototype.addListener = function addListener() { | |
_createEventOutput.call(this); | |
return this.addListener.apply(this, arguments); | |
}; | |
Force.prototype.pipe = function pipe() { | |
_createEventOutput.call(this); | |
return this.pipe.apply(this, arguments); | |
}; | |
Force.prototype.removeListener = function removeListener() { | |
return this.removeListener.apply(this, arguments); | |
}; | |
Force.prototype.unpipe = function unpipe() { | |
return this.unpipe.apply(this, arguments); | |
}; | |
module.exports = Force; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/forces/Spring',['require','exports','module','./Force','famous/math/Vector'],function(require, exports, module) { | |
var Force = require('./Force'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A force that moves a physics body to a location with a spring motion. | |
* The body can be moved to another physics body, or an anchor point. | |
* | |
* @class Spring | |
* @constructor | |
* @extends Force | |
* @param {Object} options options to set on drag | |
*/ | |
function Spring(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.disp = new Vector(0,0,0); | |
_init.call(this); | |
Force.call(this); | |
} | |
Spring.prototype = Object.create(Force.prototype); | |
Spring.prototype.constructor = Spring; | |
/** @const */ var pi = Math.PI; | |
/** | |
* @property Spring.FORCE_FUNCTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Spring.FORCE_FUNCTIONS = { | |
/** | |
* A FENE (Finitely Extensible Nonlinear Elastic) spring force | |
* see: http://en.wikipedia.org/wiki/FENE | |
* @attribute FENE | |
* @type Function | |
* @param {Number} dist current distance target is from source body | |
* @param {Number} rMax maximum range of influence | |
* @return {Number} unscaled force | |
*/ | |
FENE : function(dist, rMax) { | |
var rMaxSmall = rMax * .99; | |
var r = Math.max(Math.min(dist, rMaxSmall), -rMaxSmall); | |
return r / (1 - r * r/(rMax * rMax)); | |
}, | |
/** | |
* A Hookean spring force, linear in the displacement | |
* see: http://en.wikipedia.org/wiki/FENE | |
* @attribute FENE | |
* @type Function | |
* @param {Number} dist current distance target is from source body | |
* @return {Number} unscaled force | |
*/ | |
HOOK : function(dist) { | |
return dist; | |
} | |
}; | |
/** | |
* @property Spring.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Spring.DEFAULT_OPTIONS = { | |
/** | |
* The amount of time in milliseconds taken for one complete oscillation | |
* when there is no damping | |
* Range : [150, Infinity] | |
* @attribute period | |
* @type Number | |
* @default 300 | |
*/ | |
period : 300, | |
/** | |
* The damping of the spring. | |
* Range : [0, 1] | |
* 0 = no damping, and the spring will oscillate forever | |
* 1 = critically damped (the spring will never oscillate) | |
* @attribute dampingRatio | |
* @type Number | |
* @default 0.1 | |
*/ | |
dampingRatio : 0.1, | |
/** | |
* The rest length of the spring | |
* Range : [0, Infinity] | |
* @attribute length | |
* @type Number | |
* @default 0 | |
*/ | |
length : 0, | |
/** | |
* The maximum length of the spring (for a FENE spring) | |
* Range : [0, Infinity] | |
* @attribute length | |
* @type Number | |
* @default Infinity | |
*/ | |
maxLength : Infinity, | |
/** | |
* The location of the spring's anchor, if not another physics body | |
* | |
* @attribute anchor | |
* @type Array | |
* @optional | |
*/ | |
anchor : undefined, | |
/** | |
* The type of spring force | |
* @attribute forceFunction | |
* @type Function | |
*/ | |
forceFunction : Spring.FORCE_FUNCTIONS.HOOK | |
}; | |
function _setForceFunction(fn) { | |
this.forceFunction = fn; | |
} | |
function _calcStiffness() { | |
var options = this.options; | |
options.stiffness = Math.pow(2 * pi / options.period, 2); | |
} | |
function _calcDamping() { | |
var options = this.options; | |
options.damping = 4 * pi * options.dampingRatio / options.period; | |
} | |
function _calcEnergy(strength, dist) { | |
return 0.5 * strength * dist * dist; | |
} | |
function _init() { | |
_setForceFunction.call(this, this.options.forceFunction); | |
_calcStiffness.call(this); | |
_calcDamping.call(this); | |
} | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Spring.prototype.setOptions = function setOptions(options) { | |
if (options.anchor !== undefined) { | |
if (options.anchor.position instanceof Vector) this.options.anchor = options.anchor.position; | |
if (options.anchor instanceof Vector) this.options.anchor = options.anchor; | |
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | |
} | |
if (options.period !== undefined) this.options.period = options.period; | |
if (options.dampingRatio !== undefined) this.options.dampingRatio = options.dampingRatio; | |
if (options.length !== undefined) this.options.length = options.length; | |
if (options.forceFunction !== undefined) this.options.forceFunction = options.forceFunction; | |
if (options.maxLength !== undefined) this.options.maxLength = options.maxLength; | |
_init.call(this); | |
}; | |
/** | |
* Adds a spring force to a physics body's force accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.Body} Array of bodies to apply force to. | |
*/ | |
Spring.prototype.applyForce = function applyForce(targets, source) { | |
var force = this.force; | |
var disp = this.disp; | |
var options = this.options; | |
var stiffness = options.stiffness; | |
var damping = options.damping; | |
var restLength = options.length; | |
var lMax = options.maxLength; | |
var anchor = options.anchor || source.position; | |
for (var i = 0; i < targets.length; i++) { | |
var target = targets[i]; | |
var p2 = target.position; | |
var v2 = target.velocity; | |
anchor.sub(p2).put(disp); | |
var dist = disp.norm() - restLength; | |
if (dist === 0) return; | |
//if dampingRatio specified, then override strength and damping | |
var m = target.mass; | |
stiffness *= m; | |
damping *= m; | |
disp.normalize(stiffness * this.forceFunction(dist, lMax)) | |
.put(force); | |
if (damping) | |
if (source) force.add(v2.sub(source.velocity).mult(-damping)).put(force); | |
else force.add(v2.mult(-damping)).put(force); | |
target.applyForce(force); | |
if (source) source.applyForce(force.mult(-1)); | |
this.setEnergy(_calcEnergy(stiffness, dist)); | |
} | |
}; | |
/** | |
* Calculates the potential energy of the spring. | |
* | |
* @method getEnergy | |
* @param target {Body} The physics body attached to the spring | |
* @return energy {Number} | |
*/ | |
Spring.prototype.getEnergy = function getEnergy(target) { | |
var options = this.options; | |
var restLength = options.length; | |
var anchor = options.anchor; | |
var strength = options.stiffness; | |
var dist = anchor.sub(target.position).norm() - restLength; | |
return 0.5 * strength * dist * dist; | |
}; | |
/** | |
* Sets the anchor to a new position | |
* | |
* @method setAnchor | |
* @param anchor {Array} New anchor of the spring | |
*/ | |
Spring.prototype.setAnchor = function setAnchor(anchor) { | |
if (!this.options.anchor) this.options.anchor = new Vector(); | |
this.options.anchor.set(anchor); | |
}; | |
module.exports = Spring; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
/*global console*/ | |
define('famous/transitions/SpringTransition',['require','exports','module','famous/physics/PhysicsEngine','famous/physics/bodies/Particle','famous/physics/forces/Spring','famous/math/Vector'],function(require, exports, module) { | |
var PE = require('famous/physics/PhysicsEngine'); | |
var Particle = require('famous/physics/bodies/Particle'); | |
var Spring = require('famous/physics/forces/Spring'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* SpringTransition is a method of transitioning between two values (numbers, | |
* or arrays of numbers) with a bounce. The transition will overshoot the target | |
* state depending on the parameters of the transition. | |
* | |
* @class SpringTransition | |
* @constructor | |
* | |
* @param {Number|Array} [state=0] Initial state | |
*/ | |
function SpringTransition(state) { | |
state = state || 0; | |
this.endState = new Vector(state); | |
this.initState = new Vector(); | |
this._dimensions = undefined; | |
this._restTolerance = 1e-10; | |
this._absRestTolerance = this._restTolerance; | |
this._callback = undefined; | |
this.PE = new PE(); | |
this.spring = new Spring({anchor : this.endState}); | |
this.particle = new Particle(); | |
this.PE.addBody(this.particle); | |
this.PE.attach(this.spring, this.particle); | |
} | |
SpringTransition.SUPPORTS_MULTIPLE = 3; | |
/** | |
* @property SpringTransition.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
SpringTransition.DEFAULT_OPTIONS = { | |
/** | |
* The amount of time in milliseconds taken for one complete oscillation | |
* when there is no damping | |
* Range : [0, Infinity] | |
* | |
* @attribute period | |
* @type Number | |
* @default 300 | |
*/ | |
period : 300, | |
/** | |
* The damping of the snap. | |
* Range : [0, 1] | |
* 0 = no damping, and the spring will oscillate forever | |
* 1 = critically damped (the spring will never oscillate) | |
* | |
* @attribute dampingRatio | |
* @type Number | |
* @default 0.5 | |
*/ | |
dampingRatio : 0.5, | |
/** | |
* The initial velocity of the transition. | |
* | |
* @attribute velocity | |
* @type Number|Array | |
* @default 0 | |
*/ | |
velocity : 0 | |
}; | |
function _getEnergy() { | |
return this.particle.getEnergy() + this.spring.getEnergy(this.particle); | |
} | |
function _setParticlePosition(p) { | |
this.particle.setPosition(p); | |
} | |
function _setParticleVelocity(v) { | |
this.particle.setVelocity(v); | |
} | |
function _getParticlePosition() { | |
return (this._dimensions === 0) | |
? this.particle.getPosition1D() | |
: this.particle.getPosition(); | |
} | |
function _getParticleVelocity() { | |
return (this._dimensions === 0) | |
? this.particle.getVelocity1D() | |
: this.particle.getVelocity(); | |
} | |
function _setCallback(callback) { | |
this._callback = callback; | |
} | |
function _wake() { | |
this.PE.wake(); | |
} | |
function _sleep() { | |
this.PE.sleep(); | |
} | |
function _update() { | |
if (this.PE.isSleeping()) { | |
if (this._callback) { | |
var cb = this._callback; | |
this._callback = undefined; | |
cb(); | |
} | |
return; | |
} | |
if (_getEnergy.call(this) < this._absRestTolerance) { | |
_setParticlePosition.call(this, this.endState); | |
_setParticleVelocity.call(this, [0,0,0]); | |
_sleep.call(this); | |
} | |
} | |
function _setupDefinition(definition) { | |
// TODO fix no-console error | |
/* eslint no-console: 0 */ | |
var defaults = SpringTransition.DEFAULT_OPTIONS; | |
if (definition.period === undefined) definition.period = defaults.period; | |
if (definition.dampingRatio === undefined) definition.dampingRatio = defaults.dampingRatio; | |
if (definition.velocity === undefined) definition.velocity = defaults.velocity; | |
if (definition.period < 150) { | |
definition.period = 150; | |
console.warn('The period of a SpringTransition is capped at 150 ms. Use a SnapTransition for faster transitions'); | |
} | |
//setup spring | |
this.spring.setOptions({ | |
period : definition.period, | |
dampingRatio : definition.dampingRatio | |
}); | |
//setup particle | |
_setParticleVelocity.call(this, definition.velocity); | |
} | |
function _setAbsoluteRestTolerance() { | |
var distance = this.endState.sub(this.initState).normSquared(); | |
this._absRestTolerance = (distance === 0) | |
? this._restTolerance | |
: this._restTolerance * distance; | |
} | |
function _setTarget(target) { | |
this.endState.set(target); | |
_setAbsoluteRestTolerance.call(this); | |
} | |
/** | |
* Resets the position and velocity | |
* | |
* @method reset | |
* | |
* @param {Number|Array.Number} pos positional state | |
* @param {Number|Array} vel velocity | |
*/ | |
SpringTransition.prototype.reset = function reset(pos, vel) { | |
this._dimensions = (pos instanceof Array) | |
? pos.length | |
: 0; | |
this.initState.set(pos); | |
_setParticlePosition.call(this, pos); | |
_setTarget.call(this, pos); | |
if (vel) _setParticleVelocity.call(this, vel); | |
_setCallback.call(this, undefined); | |
}; | |
/** | |
* Getter for velocity | |
* | |
* @method getVelocity | |
* | |
* @return {Number|Array} velocity | |
*/ | |
SpringTransition.prototype.getVelocity = function getVelocity() { | |
return _getParticleVelocity.call(this); | |
}; | |
/** | |
* Setter for velocity | |
* | |
* @method setVelocity | |
* | |
* @return {Number|Array} velocity | |
*/ | |
SpringTransition.prototype.setVelocity = function setVelocity(v) { | |
this.call(this, _setParticleVelocity(v)); | |
}; | |
/** | |
* Detects whether a transition is in progress | |
* | |
* @method isActive | |
* | |
* @return {Boolean} | |
*/ | |
SpringTransition.prototype.isActive = function isActive() { | |
return !this.PE.isSleeping(); | |
}; | |
/** | |
* Halt the transition | |
* | |
* @method halt | |
*/ | |
SpringTransition.prototype.halt = function halt() { | |
this.set(this.get()); | |
}; | |
/** | |
* Get the current position of the transition | |
* | |
* @method get | |
* | |
* @return {Number|Array} state | |
*/ | |
SpringTransition.prototype.get = function get() { | |
_update.call(this); | |
return _getParticlePosition.call(this); | |
}; | |
/** | |
* Set the end position and transition, with optional callback on completion. | |
* | |
* @method set | |
* | |
* @param {Number|Array} endState Final state | |
* @param {Object} definition Transition definition | |
* @param {Function} callback Callback | |
*/ | |
SpringTransition.prototype.set = function set(endState, definition, callback) { | |
if (!definition) { | |
this.reset(endState); | |
if (callback) callback(); | |
return; | |
} | |
this._dimensions = (endState instanceof Array) | |
? endState.length | |
: 0; | |
_wake.call(this); | |
_setupDefinition.call(this, definition); | |
_setTarget.call(this, endState); | |
_setCallback.call(this, callback); | |
}; | |
module.exports = SpringTransition; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Wall',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A wall describes an infinite two-dimensional plane that physics bodies | |
* can collide with. To define a wall, you must give it a distance (from | |
* the center of the physics engine's origin, and a normal defining the plane | |
* of the wall. | |
* | |
* (wall) | |
* | | |
* | (normal) (origin) | |
* | ---> * | |
* | | |
* | (distance) | |
* ................... | |
* (100px) | |
* | |
* e.g., Wall({normal : [1,0,0], distance : 100}) | |
* would be a wall 100 pixels to the left, whose normal points right | |
* | |
* @class Wall | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.restitution] The energy ratio lost in a collision (0 = stick, 1 = elastic). Range : [0, 1] | |
* @param {Number} [options.drift] Baumgarte stabilization parameter. Makes constraints "loosely" (0) or "tightly" (1) enforced. Range : [0, 1] | |
* @param {Number} [options.slop] Amount of penetration in pixels to ignore before collision event triggers. | |
* @param {Array} [options.normal] The normal direction to the wall. | |
* @param {Number} [options.distance] The distance from the origin that the wall is placed. | |
* @param {onContact} [options.onContact] How to handle collision against the wall. | |
* | |
*/ | |
function Wall(options) { | |
this.options = Object.create(Wall.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.diff = new Vector(); | |
this.impulse = new Vector(); | |
Constraint.call(this); | |
} | |
Wall.prototype = Object.create(Constraint.prototype); | |
Wall.prototype.constructor = Wall; | |
/** | |
* @property Wall.ON_CONTACT | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Wall.ON_CONTACT = { | |
/** | |
* Physical bodies bounce off the wall | |
* @attribute REFLECT | |
*/ | |
REFLECT : 0, | |
/** | |
* Physical bodies are unaffected. Usecase is to fire events on contact. | |
* @attribute SILENT | |
*/ | |
SILENT : 1 | |
}; | |
Wall.DEFAULT_OPTIONS = { | |
restitution : 0.5, | |
drift : 0.5, | |
slop : 0, | |
normal : [1, 0, 0], | |
distance : 0, | |
onContact : Wall.ON_CONTACT.REFLECT | |
}; | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Wall.prototype.setOptions = function setOptions(options) { | |
if (options.normal !== undefined) { | |
if (options.normal instanceof Vector) this.options.normal = options.normal.clone(); | |
if (options.normal instanceof Array) this.options.normal = new Vector(options.normal); | |
} | |
if (options.restitution !== undefined) this.options.restitution = options.restitution; | |
if (options.drift !== undefined) this.options.drift = options.drift; | |
if (options.slop !== undefined) this.options.slop = options.slop; | |
if (options.distance !== undefined) this.options.distance = options.distance; | |
if (options.onContact !== undefined) this.options.onContact = options.onContact; | |
}; | |
function _getNormalVelocity(n, v) { | |
return v.dot(n); | |
} | |
function _getDistanceFromOrigin(p) { | |
var n = this.options.normal; | |
var d = this.options.distance; | |
return p.dot(n) + d; | |
} | |
function _onEnter(particle, overlap, dt) { | |
var p = particle.position; | |
var v = particle.velocity; | |
var m = particle.mass; | |
var n = this.options.normal; | |
var action = this.options.onContact; | |
var restitution = this.options.restitution; | |
var impulse = this.impulse; | |
var drift = this.options.drift; | |
var slop = -this.options.slop; | |
var gamma = 0; | |
if (this._eventOutput) { | |
var data = {particle : particle, wall : this, overlap : overlap, normal : n}; | |
this._eventOutput.emit('preCollision', data); | |
this._eventOutput.emit('collision', data); | |
} | |
switch (action) { | |
case Wall.ON_CONTACT.REFLECT: | |
var lambda = (overlap < slop) | |
? -((1 + restitution) * n.dot(v) + drift / dt * (overlap - slop)) / (m * dt + gamma) | |
: -((1 + restitution) * n.dot(v)) / (m * dt + gamma); | |
impulse.set(n.mult(dt * lambda)); | |
particle.applyImpulse(impulse); | |
particle.setPosition(p.add(n.mult(-overlap))); | |
break; | |
} | |
if (this._eventOutput) this._eventOutput.emit('postCollision', data); | |
} | |
function _onExit(particle, overlap, dt) { | |
var action = this.options.onContact; | |
var p = particle.position; | |
var n = this.options.normal; | |
if (action === Wall.ON_CONTACT.REFLECT) { | |
particle.setPosition(p.add(n.mult(-overlap))); | |
} | |
} | |
/** | |
* Adds an impulse to a physics body's velocity due to the wall constraint | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply the constraint to | |
* @param source {Body} The source of the constraint | |
* @param dt {Number} Delta time | |
*/ | |
Wall.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
var n = this.options.normal; | |
for (var i = 0; i < targets.length; i++) { | |
var particle = targets[i]; | |
var p = particle.position; | |
var v = particle.velocity; | |
var r = particle.radius || 0; | |
var overlap = _getDistanceFromOrigin.call(this, p.add(n.mult(-r))); | |
var nv = _getNormalVelocity.call(this, n, v); | |
if (overlap <= 0) { | |
if (nv < 0) _onEnter.call(this, particle, overlap, dt); | |
else _onExit.call(this, particle, overlap, dt); | |
} | |
} | |
}; | |
module.exports = Wall; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/transitions/WallTransition',['require','exports','module','famous/physics/PhysicsEngine','famous/physics/bodies/Particle','famous/physics/forces/Spring','famous/physics/constraints/Wall','famous/math/Vector'],function(require, exports, module) { | |
var PE = require('famous/physics/PhysicsEngine'); | |
var Particle = require('famous/physics/bodies/Particle'); | |
var Spring = require('famous/physics/forces/Spring'); | |
var Wall = require('famous/physics/constraints/Wall'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* WallTransition is a method of transitioning between two values (numbers, | |
* or arrays of numbers) with a bounce. Unlike a SpringTransition | |
* The transition will not overshoot the target, but bounce back against it. | |
* The behavior of the bounce is specified by the transition options. | |
* | |
* @class WallTransition | |
* @constructor | |
* | |
* @param {Number|Array} [state=0] Initial state | |
*/ | |
function WallTransition(state) { | |
state = state || 0; | |
this.endState = new Vector(state); | |
this.initState = new Vector(); | |
this.spring = new Spring({anchor : this.endState}); | |
this.wall = new Wall(); | |
this._restTolerance = 1e-10; | |
this._dimensions = 1; | |
this._absRestTolerance = this._restTolerance; | |
this._callback = undefined; | |
this.PE = new PE(); | |
this.particle = new Particle(); | |
this.PE.addBody(this.particle); | |
this.PE.attach([this.wall, this.spring], this.particle); | |
} | |
WallTransition.SUPPORTS_MULTIPLE = 3; | |
/** | |
* @property WallTransition.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
WallTransition.DEFAULT_OPTIONS = { | |
/** | |
* The amount of time in milliseconds taken for one complete oscillation | |
* when there is no damping | |
* Range : [0, Infinity] | |
* | |
* @attribute period | |
* @type Number | |
* @default 300 | |
*/ | |
period : 300, | |
/** | |
* The damping of the snap. | |
* Range : [0, 1] | |
* 0 = no damping, and the spring will oscillate forever | |
* 1 = critically damped (the spring will never oscillate) | |
* | |
* @attribute dampingRatio | |
* @type Number | |
* @default 0.5 | |
*/ | |
dampingRatio : 0.5, | |
/** | |
* The initial velocity of the transition. | |
* | |
* @attribute velocity | |
* @type Number|Array | |
* @default 0 | |
*/ | |
velocity : 0, | |
/** | |
* The percentage of momentum transferred to the wall | |
* | |
* @attribute restitution | |
* @type Number | |
* @default 0.5 | |
*/ | |
resitution : 0.5 | |
}; | |
function _getEnergy() { | |
return this.particle.getEnergy() + this.spring.getEnergy(this.particle); | |
} | |
function _setAbsoluteRestTolerance() { | |
var distance = this.endState.sub(this.initState).normSquared(); | |
this._absRestTolerance = (distance === 0) | |
? this._restTolerance | |
: this._restTolerance * distance; | |
} | |
function _wake() { | |
this.PE.wake(); | |
} | |
function _sleep() { | |
this.PE.sleep(); | |
} | |
function _setTarget(target) { | |
this.endState.set(target); | |
var dist = this.endState.sub(this.initState).norm(); | |
this.wall.setOptions({ | |
distance : this.endState.norm(), | |
normal : (dist === 0) | |
? this.particle.velocity.normalize(-1) | |
: this.endState.sub(this.initState).normalize(-1) | |
}); | |
_setAbsoluteRestTolerance.call(this); | |
} | |
function _setParticlePosition(p) { | |
this.particle.position.set(p); | |
} | |
function _setParticleVelocity(v) { | |
this.particle.velocity.set(v); | |
} | |
function _getParticlePosition() { | |
return (this._dimensions === 0) | |
? this.particle.getPosition1D() | |
: this.particle.getPosition(); | |
} | |
function _getParticleVelocity() { | |
return (this._dimensions === 0) | |
? this.particle.getVelocity1D() | |
: this.particle.getVelocity(); | |
} | |
function _setCallback(callback) { | |
this._callback = callback; | |
} | |
function _update() { | |
if (this.PE.isSleeping()) { | |
if (this._callback) { | |
var cb = this._callback; | |
this._callback = undefined; | |
cb(); | |
} | |
return; | |
} | |
var energy = _getEnergy.call(this); | |
if (energy < this._absRestTolerance) { | |
_sleep.call(this); | |
_setParticlePosition.call(this, this.endState); | |
_setParticleVelocity.call(this, [0,0,0]); | |
} | |
} | |
function _setupDefinition(def) { | |
var defaults = WallTransition.DEFAULT_OPTIONS; | |
if (def.period === undefined) def.period = defaults.period; | |
if (def.dampingRatio === undefined) def.dampingRatio = defaults.dampingRatio; | |
if (def.velocity === undefined) def.velocity = defaults.velocity; | |
if (def.restitution === undefined) def.restitution = defaults.restitution; | |
//setup spring | |
this.spring.setOptions({ | |
period : def.period, | |
dampingRatio : def.dampingRatio | |
}); | |
//setup wall | |
this.wall.setOptions({ | |
restitution : def.restitution | |
}); | |
//setup particle | |
_setParticleVelocity.call(this, def.velocity); | |
} | |
/** | |
* Resets the state and velocity | |
* | |
* @method reset | |
* | |
* @param {Number|Array} state State | |
* @param {Number|Array} [velocity] Velocity | |
*/ | |
WallTransition.prototype.reset = function reset(state, velocity) { | |
this._dimensions = (state instanceof Array) | |
? state.length | |
: 0; | |
this.initState.set(state); | |
_setParticlePosition.call(this, state); | |
if (velocity) _setParticleVelocity.call(this, velocity); | |
_setTarget.call(this, state); | |
_setCallback.call(this, undefined); | |
}; | |
/** | |
* Getter for velocity | |
* | |
* @method getVelocity | |
* | |
* @return velocity {Number|Array} | |
*/ | |
WallTransition.prototype.getVelocity = function getVelocity() { | |
return _getParticleVelocity.call(this); | |
}; | |
/** | |
* Setter for velocity | |
* | |
* @method setVelocity | |
* | |
* @return velocity {Number|Array} | |
*/ | |
WallTransition.prototype.setVelocity = function setVelocity(velocity) { | |
this.call(this, _setParticleVelocity(velocity)); | |
}; | |
/** | |
* Detects whether a transition is in progress | |
* | |
* @method isActive | |
* | |
* @return {Boolean} | |
*/ | |
WallTransition.prototype.isActive = function isActive() { | |
return !this.PE.isSleeping(); | |
}; | |
/** | |
* Halt the transition | |
* | |
* @method halt | |
*/ | |
WallTransition.prototype.halt = function halt() { | |
this.set(this.get()); | |
}; | |
/** | |
* Getter | |
* | |
* @method get | |
* | |
* @return state {Number|Array} | |
*/ | |
WallTransition.prototype.get = function get() { | |
_update.call(this); | |
return _getParticlePosition.call(this); | |
}; | |
/** | |
* Set the end position and transition, with optional callback on completion. | |
* | |
* @method set | |
* | |
* @param state {Number|Array} Final state | |
* @param [definition] {Object} Transition definition | |
* @param [callback] {Function} Callback | |
*/ | |
WallTransition.prototype.set = function set(state, definition, callback) { | |
if (!definition) { | |
this.reset(state); | |
if (callback) callback(); | |
return; | |
} | |
this._dimensions = (state instanceof Array) | |
? state.length | |
: 0; | |
_wake.call(this); | |
_setupDefinition.call(this, definition); | |
_setTarget.call(this, state); | |
_setCallback.call(this, callback); | |
}; | |
module.exports = WallTransition; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/CanvasSurface',['require','exports','module','famous/core/Surface'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
/** | |
* A surface containing an HTML5 Canvas element. | |
* This extends the Surface class. | |
* | |
* @class CanvasSurface | |
* @extends Surface | |
* @constructor | |
* @param {Object} [options] overrides of default options | |
* @param {Array.Number} [options.canvasSize] [width, height] for document element | |
*/ | |
function CanvasSurface(options) { | |
if (options && options.canvasSize) this._canvasSize = options.canvasSize; | |
Surface.apply(this, arguments); | |
if (!this._canvasSize) this._canvasSize = this.getSize(); | |
this._backBuffer = document.createElement('canvas'); | |
if (this._canvasSize) { | |
this._backBuffer.width = this._canvasSize[0]; | |
this._backBuffer.height = this._canvasSize[1]; | |
} | |
this._contextId = undefined; | |
} | |
CanvasSurface.prototype = Object.create(Surface.prototype); | |
CanvasSurface.prototype.constructor = CanvasSurface; | |
CanvasSurface.prototype.elementType = 'canvas'; | |
CanvasSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Set inner document content. Note that this is a noop for CanvasSurface. | |
* | |
* @method setContent | |
* | |
*/ | |
CanvasSurface.prototype.setContent = function setContent() {}; | |
/** | |
* Place the document element this component manages into the document. | |
* This will draw the content to the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
CanvasSurface.prototype.deploy = function deploy(target) { | |
if (this._canvasSize) { | |
target.width = this._canvasSize[0]; | |
target.height = this._canvasSize[1]; | |
} | |
if (this._contextId === '2d') { | |
target.getContext(this._contextId).drawImage(this._backBuffer, 0, 0); | |
this._backBuffer.width = 0; | |
this._backBuffer.height = 0; | |
} | |
}; | |
/** | |
* Remove this component and contained content from the document | |
* | |
* @private | |
* @method recall | |
* | |
* @param {Node} target node to which the component was deployed | |
*/ | |
CanvasSurface.prototype.recall = function recall(target) { | |
var size = this.getSize(); | |
this._backBuffer.width = target.width; | |
this._backBuffer.height = target.height; | |
if (this._contextId === '2d') { | |
this._backBuffer.getContext(this._contextId).drawImage(target, 0, 0); | |
target.width = 0; | |
target.height = 0; | |
} | |
}; | |
/** | |
* Returns the canvas element's context | |
* | |
* @method getContext | |
* @param {string} contextId context identifier | |
*/ | |
CanvasSurface.prototype.getContext = function getContext(contextId) { | |
this._contextId = contextId; | |
return this._currTarget ? this._currTarget.getContext(contextId) : this._backBuffer.getContext(contextId); | |
}; | |
/** | |
* Set the size of the surface and canvas element. | |
* | |
* @method setSize | |
* @param {Array.number} size [width, height] of surface | |
* @param {Array.number} canvasSize [width, height] of canvas surface | |
*/ | |
CanvasSurface.prototype.setSize = function setSize(size, canvasSize) { | |
Surface.prototype.setSize.apply(this, arguments); | |
if (canvasSize) this._canvasSize = [canvasSize[0], canvasSize[1]]; | |
if (this._currTarget) { | |
this._currTarget.width = this._canvasSize[0]; | |
this._currTarget.height = this._canvasSize[1]; | |
} | |
}; | |
module.exports = CanvasSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/ContainerSurface',['require','exports','module','famous/core/Surface','famous/core/Context'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
var Context = require('famous/core/Context'); | |
/** | |
* ContainerSurface is an object designed to contain surfaces and | |
* set properties to be applied to all of them at once. | |
* This extends the Surface class. | |
* A container surface will enforce these properties on the | |
* surfaces it contains: | |
* | |
* size (clips contained surfaces to its own width and height); | |
* | |
* origin; | |
* | |
* its own opacity and transform, which will be automatically | |
* applied to all Surfaces contained directly and indirectly. | |
* | |
* @class ContainerSurface | |
* @extends Surface | |
* @constructor | |
* @param {Array.Number} [options.size] [width, height] in pixels | |
* @param {Array.string} [options.classes] CSS classes to set on all inner content | |
* @param {Array} [options.properties] string dictionary of HTML attributes to set on target div | |
* @param {string} [options.content] inner (HTML) content of surface (should not be used) | |
*/ | |
function ContainerSurface(options) { | |
Surface.call(this, options); | |
this._container = document.createElement('div'); | |
this._container.classList.add('famous-group'); | |
this._container.classList.add('famous-container-group'); | |
this._shouldRecalculateSize = false; | |
this.context = new Context(this._container); | |
this.setContent(this._container); | |
} | |
ContainerSurface.prototype = Object.create(Surface.prototype); | |
ContainerSurface.prototype.constructor = ContainerSurface; | |
ContainerSurface.prototype.elementType = 'div'; | |
ContainerSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Add renderables to this object's render tree | |
* | |
* @method add | |
* | |
* @param {Object} obj renderable object | |
* @return {RenderNode} RenderNode wrapping this object, if not already a RenderNode | |
*/ | |
ContainerSurface.prototype.add = function add() { | |
return this.context.add.apply(this.context, arguments); | |
}; | |
/** | |
* Return spec for this surface. Note: Can result in a size recalculation. | |
* | |
* @private | |
* @method render | |
* | |
* @return {Object} render spec for this surface (spec id) | |
*/ | |
ContainerSurface.prototype.render = function render() { | |
if (this._sizeDirty) this._shouldRecalculateSize = true; | |
return Surface.prototype.render.apply(this, arguments); | |
}; | |
/** | |
* Place the document element this component manages into the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
ContainerSurface.prototype.deploy = function deploy() { | |
this._shouldRecalculateSize = true; | |
return Surface.prototype.deploy.apply(this, arguments); | |
}; | |
/** | |
* 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 | |
* @param {Transform} transform unused TODO | |
* @param {Number} opacity unused TODO | |
* @param {Array.Number} origin unused TODO | |
* @param {Array.Number} size unused TODO | |
* @return {undefined} TODO returns an undefined value | |
*/ | |
ContainerSurface.prototype.commit = function commit(context, transform, opacity, origin, size) { | |
var previousSize = this._size ? [this._size[0], this._size[1]] : null; | |
var result = Surface.prototype.commit.apply(this, arguments); | |
if (this._shouldRecalculateSize || (previousSize && (this._size[0] !== previousSize[0] || this._size[1] !== previousSize[1]))) { | |
this.context.setSize(); | |
this._shouldRecalculateSize = false; | |
} | |
this.context.update(); | |
return result; | |
}; | |
module.exports = ContainerSurface; | |
}); | |
define('famous/surfaces/FormContainerSurface',['require','exports','module','./ContainerSurface'],function(require, exports, module) { | |
var ContainerSurface = require('./ContainerSurface'); | |
function FormContainerSurface(options) { | |
if (options) this._method = options.method || ''; | |
ContainerSurface.apply(this, arguments); | |
} | |
FormContainerSurface.prototype = Object.create(ContainerSurface.prototype); | |
FormContainerSurface.prototype.constructor = FormContainerSurface; | |
FormContainerSurface.prototype.elementType = 'form'; | |
FormContainerSurface.prototype.deploy = function deploy(target) { | |
if (this._method) target.method = this._method; | |
return ContainerSurface.prototype.deploy.apply(this, arguments); | |
}; | |
module.exports = FormContainerSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/ImageSurface',['require','exports','module','famous/core/Surface'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
/** | |
* A surface containing image content. | |
* This extends the Surface class. | |
* | |
* @class ImageSurface | |
* | |
* @extends Surface | |
* @constructor | |
* @param {Object} [options] overrides of default options | |
*/ | |
function ImageSurface(options) { | |
this._imageUrl = undefined; | |
Surface.apply(this, arguments); | |
} | |
ImageSurface.prototype = Object.create(Surface.prototype); | |
ImageSurface.prototype.constructor = ImageSurface; | |
ImageSurface.prototype.elementType = 'img'; | |
ImageSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Set content URL. This will cause a re-rendering. | |
* @method setContent | |
* @param {string} imageUrl | |
*/ | |
ImageSurface.prototype.setContent = function setContent(imageUrl) { | |
this._imageUrl = imageUrl; | |
this._contentDirty = true; | |
}; | |
/** | |
* Place the document element that this component manages into the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
ImageSurface.prototype.deploy = function deploy(target) { | |
target.src = this._imageUrl || ''; | |
}; | |
/** | |
* Remove this component and contained content from the document | |
* | |
* @private | |
* @method recall | |
* | |
* @param {Node} target node to which the component was deployed | |
*/ | |
ImageSurface.prototype.recall = function recall(target) { | |
target.src = ''; | |
}; | |
module.exports = ImageSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/InputSurface',['require','exports','module','famous/core/Surface'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
/** | |
* A Famo.us surface in the form of an HTML input element. | |
* This extends the Surface class. | |
* | |
* @class InputSurface | |
* @extends Surface | |
* @constructor | |
* @param {Object} [options] overrides of default options | |
* @param {string} [options.placeholder] placeholder text hint that describes the expected value of an <input> element | |
* @param {string} [options.type] specifies the type of element to display (e.g. 'datetime', 'text', 'button', etc.) | |
* @param {string} [options.value] value of text | |
*/ | |
function InputSurface(options) { | |
this._placeholder = options.placeholder || ''; | |
this._value = options.value || ''; | |
this._type = options.type || 'text'; | |
this._name = options.name || ''; | |
Surface.apply(this, arguments); | |
this.on('click', this.focus.bind(this)); | |
window.addEventListener('click', function(event) { | |
if (event.target !== this._currTarget) this.blur(); | |
}.bind(this)); | |
} | |
InputSurface.prototype = Object.create(Surface.prototype); | |
InputSurface.prototype.constructor = InputSurface; | |
InputSurface.prototype.elementType = 'input'; | |
InputSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Set placeholder text. Note: Triggers a repaint. | |
* | |
* @method setPlaceholder | |
* @param {string} str Value to set the placeholder to. | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.setPlaceholder = function setPlaceholder(str) { | |
this._placeholder = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Focus on the current input, pulling up the keyboard on mobile. | |
* | |
* @method focus | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.focus = function focus() { | |
if (this._currTarget) this._currTarget.focus(); | |
return this; | |
}; | |
/** | |
* Blur the current input, hiding the keyboard on mobile. | |
* | |
* @method blur | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.blur = function blur() { | |
if (this._currTarget) this._currTarget.blur(); | |
return this; | |
}; | |
/** | |
* Set the placeholder conent. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setValue | |
* @param {string} str Value to set the main input value to. | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.setValue = function setValue(str) { | |
this._value = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Set the type of element to display conent. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setType | |
* @param {string} str type of the input surface (e.g. 'button', 'text') | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.setType = function setType(str) { | |
this._type = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Get the value of the inner content of the element (e.g. the entered text) | |
* | |
* @method getValue | |
* @return {string} value of element | |
*/ | |
InputSurface.prototype.getValue = function getValue() { | |
if (this._currTarget) { | |
return this._currTarget.value; | |
} | |
else { | |
return this._value; | |
} | |
}; | |
/** | |
* Set the name attribute of the element. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setName | |
* @param {string} str element name | |
* @return {InputSurface} this, allowing method chaining. | |
*/ | |
InputSurface.prototype.setName = function setName(str) { | |
this._name = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Get the name attribute of the element. | |
* | |
* @method getName | |
* @return {string} name of element | |
*/ | |
InputSurface.prototype.getName = function getName() { | |
return this._name; | |
}; | |
/** | |
* Place the document element this component manages into the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
InputSurface.prototype.deploy = function deploy(target) { | |
if (this._placeholder !== '') target.placeholder = this._placeholder; | |
target.value = this._value; | |
target.type = this._type; | |
target.name = this._name; | |
}; | |
module.exports = InputSurface; | |
}); | |
define('famous/surfaces/SubmitInputSurface',['require','exports','module','./InputSurface'],function(require, exports, module) { | |
var InputSurface = require('./InputSurface'); | |
function SubmitInputSurface(options) { | |
InputSurface.apply(this, arguments); | |
this._type = 'submit'; | |
if (options && options.onClick) this.setOnClick(options.onClick); | |
} | |
SubmitInputSurface.prototype = Object.create(InputSurface.prototype); | |
SubmitInputSurface.prototype.constructor = SubmitInputSurface; | |
SubmitInputSurface.prototype.setOnClick = function(onClick) { | |
this.onClick = onClick; | |
}; | |
SubmitInputSurface.prototype.deploy = function deploy(target) { | |
if (this.onclick) target.onClick = this.onClick; | |
InputSurface.prototype.deploy.apply(this, arguments); | |
}; | |
module.exports = SubmitInputSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/TextareaSurface',['require','exports','module','famous/core/Surface'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
/** | |
* A Famo.us surface in the form of an HTML textarea element. | |
* This extends the Surface class. | |
* | |
* @class TextareaSurface | |
* @extends Surface | |
* @constructor | |
* @param {Object} [options] overrides of default options | |
* @param {string} [options.placeholder] placeholder text hint that describes the expected value of an textarea element | |
* @param {string} [options.value] value of text | |
* @param {string} [options.name] specifies the name of textarea | |
* @param {string} [options.wrap] specify 'hard' or 'soft' wrap for textarea | |
* @param {number} [options.cols] number of columns in textarea | |
* @param {number} [options.rows] number of rows in textarea | |
*/ | |
function TextareaSurface(options) { | |
this._placeholder = options.placeholder || ''; | |
this._value = options.value || ''; | |
this._name = options.name || ''; | |
this._wrap = options.wrap || ''; | |
this._cols = options.cols || ''; | |
this._rows = options.rows || ''; | |
Surface.apply(this, arguments); | |
this.on('click', this.focus.bind(this)); | |
} | |
TextareaSurface.prototype = Object.create(Surface.prototype); | |
TextareaSurface.prototype.constructor = TextareaSurface; | |
TextareaSurface.prototype.elementType = 'textarea'; | |
TextareaSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Set placeholder text. Note: Triggers a repaint. | |
* | |
* @method setPlaceholder | |
* @param {string} str Value to set the placeholder to. | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setPlaceholder = function setPlaceholder(str) { | |
this._placeholder = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Focus on the current input, pulling up the keyboard on mobile. | |
* | |
* @method focus | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.focus = function focus() { | |
if (this._currTarget) this._currTarget.focus(); | |
return this; | |
}; | |
/** | |
* Blur the current input, hiding the keyboard on mobile. | |
* | |
* @method focus | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.blur = function blur() { | |
if (this._currTarget) this._currTarget.blur(); | |
return this; | |
}; | |
/** | |
* Set the value of textarea. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setValue | |
* @param {string} str Value to set the main textarea value to. | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setValue = function setValue(str) { | |
this._value = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Get the value of the inner content of the textarea (e.g. the entered text) | |
* | |
* @method getValue | |
* @return {string} value of element | |
*/ | |
TextareaSurface.prototype.getValue = function getValue() { | |
if (this._currTarget) { | |
return this._currTarget.value; | |
} | |
else { | |
return this._value; | |
} | |
}; | |
/** | |
* Set the name attribute of the element. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setName | |
* @param {string} str element name | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setName = function setName(str) { | |
this._name = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Get the name attribute of the element. | |
* | |
* @method getName | |
* @return {string} name of element | |
*/ | |
TextareaSurface.prototype.getName = function getName() { | |
return this._name; | |
}; | |
/** | |
* Set the wrap of textarea. | |
* Note: Triggers a repaint next tick. | |
* | |
* @method setWrap | |
* @param {string} str wrap of the textarea surface (e.g. 'soft', 'hard') | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setWrap = function setWrap(str) { | |
this._wrap = str; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Set the number of columns visible in the textarea. | |
* Note: Overridden by surface size; set width to true. (eg. size: [true, *]) | |
* Triggers a repaint next tick. | |
* | |
* @method setColumns | |
* @param {number} num columns in textarea surface | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setColumns = function setColumns(num) { | |
this._cols = num; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Set the number of rows visible in the textarea. | |
* Note: Overridden by surface size; set height to true. (eg. size: [*, true]) | |
* Triggers a repaint next tick. | |
* | |
* @method setRows | |
* @param {number} num rows in textarea surface | |
* @return {TextareaSurface} this, allowing method chaining. | |
*/ | |
TextareaSurface.prototype.setRows = function setRows(num) { | |
this._rows = num; | |
this._contentDirty = true; | |
return this; | |
}; | |
/** | |
* Place the document element this component manages into the document. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
TextareaSurface.prototype.deploy = function deploy(target) { | |
if (this._placeholder !== '') target.placeholder = this._placeholder; | |
if (this._value !== '') target.value = this._value; | |
if (this._name !== '') target.name = this._name; | |
if (this._wrap !== '') target.wrap = this._wrap; | |
if (this._cols !== '') target.cols = this._cols; | |
if (this._rows !== '') target.rows = this._rows; | |
}; | |
module.exports = TextareaSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/surfaces/VideoSurface',['require','exports','module','famous/core/Surface'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
/** | |
* Creates a famous surface containing video content. Currently adding | |
* controls and manipulating the video are not supported through the | |
* surface interface, but can be accomplished via standard JavaScript | |
* manipulation of the video DOM element. | |
* This extends the Surface class. | |
* | |
* @class VideoSurface | |
* @extends 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 inner content | |
* @param {Array} [options.properties] string dictionary of HTML attributes to set on target div | |
* @param {string} [options.content] inner (HTML) content of surface | |
* @param {boolean} [options.autoplay] autoplay | |
*/ | |
function VideoSurface(options) { | |
this._videoUrl = undefined; | |
this.options = Object.create(VideoSurface.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
Surface.apply(this, arguments); | |
} | |
VideoSurface.prototype = Object.create(Surface.prototype); | |
VideoSurface.prototype.constructor = VideoSurface; | |
VideoSurface.DEFAULT_OPTIONS = { | |
autoplay: false | |
}; | |
VideoSurface.prototype.elementType = 'video'; | |
VideoSurface.prototype.elementClass = 'famous-surface'; | |
/** | |
* Set internal options, overriding any default options | |
* | |
* @method setOptions | |
* | |
* @param {Object} [options] overrides of default options | |
* @param {Boolean} [options.autoplay] HTML autoplay | |
*/ | |
VideoSurface.prototype.setOptions = function setOptions(options) { | |
for (var key in VideoSurface.DEFAULT_OPTIONS) { | |
if (options[key] !== undefined) this.options[key] = options[key]; | |
} | |
}; | |
/** | |
* Set url of the video. | |
* | |
* @method setContent | |
* @param {string} videoUrl URL | |
*/ | |
VideoSurface.prototype.setContent = function setContent(videoUrl) { | |
this._videoUrl = videoUrl; | |
this._contentDirty = true; | |
}; | |
/** | |
* Place the document element this component manages into the document. | |
* Note: In the case of VideoSurface, simply changes the options on the target. | |
* | |
* @private | |
* @method deploy | |
* @param {Node} target document parent of this container | |
*/ | |
VideoSurface.prototype.deploy = function deploy(target) { | |
target.src = this._videoUrl; | |
target.autoplay = this.options.autoplay; | |
}; | |
/** | |
* Remove this component and contained content from the document. | |
* Note: This doesn't actually remove the <video> element from the | |
* document. | |
* @private | |
* @method recall | |
* | |
* @param {Node} target node to which the component was deployed | |
*/ | |
VideoSurface.prototype.recall = function recall(target) { | |
target.src = ''; | |
}; | |
module.exports = VideoSurface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/utilities/KeyCodes',['require','exports','module'],function(require, exports, module) { | |
/** | |
* Collection to map keyboard codes in plain english | |
* | |
* @class KeyCodes | |
* @static | |
*/ | |
var KeyCodes = { | |
0 : 48, | |
1 : 49, | |
2 : 50, | |
3 : 51, | |
4 : 52, | |
5 : 53, | |
6 : 54, | |
7 : 55, | |
8 : 56, | |
9 : 57, | |
a : 97, | |
b : 98, | |
c : 99, | |
d : 100, | |
e : 101, | |
f : 102, | |
g : 103, | |
h : 104, | |
i : 105, | |
j : 106, | |
k : 107, | |
l : 108, | |
m : 109, | |
n : 110, | |
o : 111, | |
p : 112, | |
q : 113, | |
r : 114, | |
s : 115, | |
t : 116, | |
u : 117, | |
v : 118, | |
w : 119, | |
x : 120, | |
y : 121, | |
z : 122, | |
A : 65, | |
B : 66, | |
C : 67, | |
D : 68, | |
E : 69, | |
F : 70, | |
G : 71, | |
H : 72, | |
I : 73, | |
J : 74, | |
K : 75, | |
L : 76, | |
M : 77, | |
N : 78, | |
O : 79, | |
P : 80, | |
Q : 81, | |
R : 82, | |
S : 83, | |
T : 84, | |
U : 85, | |
V : 86, | |
W : 87, | |
X : 88, | |
Y : 89, | |
Z : 90, | |
ENTER : 13, | |
LEFT_ARROW: 37, | |
RIGHT_ARROW: 39, | |
UP_ARROW: 38, | |
DOWN_ARROW: 40, | |
SPACE: 32, | |
SHIFT: 16, | |
TAB: 9 | |
}; | |
module.exports = KeyCodes; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
// TODO fix func-style | |
/*eslint func-style: [0, "declaration"] */ | |
define('famous/utilities/Timer',['require','exports','module','famous/core/Engine'],function(require, exports, module) { | |
/** | |
* An internal library to reproduce javascript time-based scheduling. | |
* Using standard javascript setTimeout methods can have a negative performance impact | |
* when combined with the Famous rendering process, so instead require Timer and call | |
* Timer.setTimeout, Timer.setInterval, etc. | |
* | |
* @class Timer | |
* @constructor | |
*/ | |
var FamousEngine = require('famous/core/Engine'); | |
var _event = 'prerender'; | |
var getTime = (window.performance) ? | |
function() { | |
return window.performance.now(); | |
} | |
: function() { | |
return Date.now(); | |
}; | |
/** | |
* Add a function to be run on every prerender | |
* | |
* @method addTimerFunction | |
* | |
* @param {function} fn function to be run every prerender | |
* | |
* @return {function} function passed in as parameter | |
*/ | |
function addTimerFunction(fn) { | |
FamousEngine.on(_event, fn); | |
return fn; | |
} | |
/** | |
* Wraps a function to be invoked after a certain amount of time. | |
* After a set duration has passed, it executes the function and | |
* removes it as a listener to 'prerender'. | |
* | |
* @method setTimeout | |
* | |
* @param {function} fn function to be run after a specified duration | |
* @param {number} duration milliseconds from now to execute the function | |
* | |
* @return {function} function passed in as parameter | |
*/ | |
function setTimeout(fn, duration) { | |
var t = getTime(); | |
var callback = function() { | |
var t2 = getTime(); | |
if (t2 - t >= duration) { | |
fn.apply(this, arguments); | |
FamousEngine.removeListener(_event, callback); | |
} | |
}; | |
return addTimerFunction(callback); | |
} | |
/** | |
* Wraps a function to be invoked after a certain amount of time. | |
* After a set duration has passed, it executes the function and | |
* resets the execution time. | |
* | |
* @method setInterval | |
* | |
* @param {function} fn function to be run after a specified duration | |
* @param {number} duration interval to execute function in milliseconds | |
* | |
* @return {function} function passed in as parameter | |
*/ | |
function setInterval(fn, duration) { | |
var t = getTime(); | |
var callback = function() { | |
var t2 = getTime(); | |
if (t2 - t >= duration) { | |
fn.apply(this, arguments); | |
t = getTime(); | |
} | |
}; | |
return addTimerFunction(callback); | |
} | |
/** | |
* Wraps a function to be invoked after a certain amount of prerender ticks. | |
* Similar use to setTimeout but tied to the engine's run speed. | |
* | |
* @method after | |
* | |
* @param {function} fn function to be run after a specified amount of ticks | |
* @param {number} numTicks number of prerender frames to wait | |
* | |
* @return {function} function passed in as parameter | |
*/ | |
function after(fn, numTicks) { | |
if (numTicks === undefined) return undefined; | |
var callback = function() { | |
numTicks--; | |
if (numTicks <= 0) { //in case numTicks is fraction or negative | |
fn.apply(this, arguments); | |
clear(callback); | |
} | |
}; | |
return addTimerFunction(callback); | |
} | |
/** | |
* Wraps a function to be continually invoked after a certain amount of prerender ticks. | |
* Similar use to setInterval but tied to the engine's run speed. | |
* | |
* @method every | |
* | |
* @param {function} fn function to be run after a specified amount of ticks | |
* @param {number} numTicks number of prerender frames to wait | |
* | |
* @return {function} function passed in as parameter | |
*/ | |
function every(fn, numTicks) { | |
numTicks = numTicks || 1; | |
var initial = numTicks; | |
var callback = function() { | |
numTicks--; | |
if (numTicks <= 0) { //in case numTicks is fraction or negative | |
fn.apply(this, arguments); | |
numTicks = initial; | |
} | |
}; | |
return addTimerFunction(callback); | |
} | |
/** | |
* Remove a function that gets called every prerender | |
* | |
* @method clear | |
* | |
* @param {function} fn event linstener | |
*/ | |
function clear(fn) { | |
FamousEngine.removeListener(_event, fn); | |
} | |
/** | |
* Executes a function after a certain amount of time. Makes sure | |
* the function is not run multiple times. | |
* | |
* @method debounce | |
* | |
* @param {function} func function to run after certain amount of time | |
* @param {number} wait amount of time | |
* | |
* @return {function} function that is not able to debounce | |
*/ | |
function debounce(func, wait) { | |
var timeout; | |
var ctx; | |
var timestamp; | |
var result; | |
var args; | |
return function() { | |
ctx = this; | |
args = arguments; | |
timestamp = getTime(); | |
var fn = function() { | |
var last = getTime - timestamp; | |
if (last < wait) { | |
timeout = setTimeout(fn, wait - last); | |
} else { | |
timeout = null; | |
result = func.apply(ctx, args); | |
} | |
}; | |
clear(timeout); | |
timeout = setTimeout(fn, wait); | |
return result; | |
}; | |
} | |
module.exports = { | |
setTimeout : setTimeout, | |
setInterval : setInterval, | |
debounce : debounce, | |
after : after, | |
every : every, | |
clear : clear | |
}; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/ContextualView',['require','exports','module','famous/core/Entity','famous/core/Transform','famous/core/EventHandler','famous/core/OptionsManager'],function(require, exports, module) { | |
var Entity = require('famous/core/Entity'); | |
var Transform = require('famous/core/Transform'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* ContextualView is an interface for creating views that need to | |
* be aware of their parent's transform, size, and/or origin. | |
* Consists of a OptionsManager paired with an input EventHandler | |
* and an output EventHandler. Meant to be extended by the developer. | |
* @class ContextualView | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
*/ | |
function ContextualView(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS || ContextualView.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this._id = Entity.register(this); | |
} | |
ContextualView.DEFAULT_OPTIONS = {}; | |
/** | |
* Patches the ContextualLayout instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the ContextualLayout instance. | |
*/ | |
ContextualView.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
/** | |
* Returns ContextualLayout instance's options. | |
* | |
* @method setOptions | |
* @return {Options} options The instance's object of configurable options. | |
*/ | |
ContextualView.prototype.getOptions = function getOptions() { | |
return this._optionsManager.getOptions(); | |
}; | |
/** | |
* Return the registers Entity id for the ContextualView. | |
* | |
* @private | |
* @method render | |
* @return {Number} Registered Entity id | |
*/ | |
ContextualView.prototype.render = function render() { | |
return this._id; | |
}; | |
/** | |
* 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 | |
*/ | |
ContextualView.prototype.commit = function commit(context) {}; | |
module.exports = ContextualView; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/SequentialLayout',['require','exports','module','famous/core/OptionsManager','famous/core/Transform','famous/core/ViewSequence','famous/utilities/Utility'],function(require, exports, module) { | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var Transform = require('famous/core/Transform'); | |
var ViewSequence = require('famous/core/ViewSequence'); | |
var Utility = require('famous/utilities/Utility'); | |
/** | |
* SequentialLayout will lay out a collection of renderables sequentially in the specified direction. | |
* @class SequentialLayout | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.direction=Utility.Direction.Y] Using the direction helper found in the famous Utility | |
* module, this option will lay out the SequentialLayout instance's renderables either horizontally | |
* (x) or vertically (y). Utility's direction is essentially either zero (X) or one (Y), so feel free | |
* to just use integers as well. | |
* @param {Array.Number} [options.defaultItemSize=[50, 50]] In the case where a renderable layed out | |
* under SequentialLayout's control doesen't have a getSize method, SequentialLayout will assign it | |
* this default size. (Commonly a case with Views). | |
*/ | |
function SequentialLayout(options) { | |
this._items = null; | |
this._size = null; | |
this._outputFunction = SequentialLayout.DEFAULT_OUTPUT_FUNCTION; | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
this.optionsManager = new OptionsManager(this.options); | |
this._itemsCache = []; | |
this._outputCache = { | |
size: null, | |
target: this._itemsCache | |
}; | |
if (options) this.setOptions(options); | |
} | |
SequentialLayout.DEFAULT_OPTIONS = { | |
direction: Utility.Direction.Y, | |
itemSpacing: 0, | |
defaultItemSize: [50, 50] | |
}; | |
SequentialLayout.DEFAULT_OUTPUT_FUNCTION = function DEFAULT_OUTPUT_FUNCTION(input, offset, index) { | |
var transform = (this.options.direction === Utility.Direction.X) ? Transform.translate(offset, 0) : Transform.translate(0, offset); | |
return { | |
transform: transform, | |
target: input.render() | |
}; | |
}; | |
/** | |
* Returns the width and the height of the SequentialLayout instance. | |
* | |
* @method getSize | |
* @return {Array} A two value array of the SequentialLayout instance's current width and height (in that order). | |
*/ | |
SequentialLayout.prototype.getSize = function getSize() { | |
if (!this._size) this.render(); // hack size in | |
return this._size; | |
}; | |
/** | |
* Sets the collection of renderables under the SequentialLayout instance's control. | |
* | |
* @method sequenceFrom | |
* @param {Array|ViewSequence} items Either an array of renderables or a Famous viewSequence. | |
* @chainable | |
*/ | |
SequentialLayout.prototype.sequenceFrom = function sequenceFrom(items) { | |
if (items instanceof Array) items = new ViewSequence(items); | |
this._items = items; | |
return this; | |
}; | |
/** | |
* Patches the SequentialLayout instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the SequentialLayout instance. | |
* @chainable | |
*/ | |
SequentialLayout.prototype.setOptions = function setOptions(options) { | |
this.optionsManager.setOptions.apply(this.optionsManager, arguments); | |
return this; | |
}; | |
/** | |
* setOutputFunction is used to apply a user-defined output transform on each processed renderable. | |
* For a good example, check out SequentialLayout's own DEFAULT_OUTPUT_FUNCTION in the code. | |
* | |
* @method setOutputFunction | |
* @param {Function} outputFunction An output processer for each renderable in the SequentialLayout | |
* instance. | |
* @chainable | |
*/ | |
SequentialLayout.prototype.setOutputFunction = function setOutputFunction(outputFunction) { | |
this._outputFunction = outputFunction; | |
return this; | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
SequentialLayout.prototype.render = function render() { | |
var length = 0; | |
var girth = 0; | |
var lengthDim = (this.options.direction === Utility.Direction.X) ? 0 : 1; | |
var girthDim = (this.options.direction === Utility.Direction.X) ? 1 : 0; | |
var currentNode = this._items; | |
var result = this._itemsCache; | |
var i = 0; | |
while (currentNode) { | |
var item = currentNode.get(); | |
if (!item) break; | |
var itemSize; | |
if (item && item.getSize) itemSize = item.getSize(); | |
if (!itemSize) itemSize = this.options.defaultItemSize; | |
if (itemSize[girthDim] !== true) girth = Math.max(girth, itemSize[girthDim]); | |
var output = this._outputFunction.call(this, item, length, i); | |
result[i] = output; | |
if (itemSize[lengthDim] && (itemSize[lengthDim] !== true)) length += itemSize[lengthDim] + this.options.itemSpacing; | |
currentNode = currentNode.getNext(); | |
i++; | |
} | |
this._itemsCache.splice(i); | |
if (!girth) girth = undefined; | |
if (!this._size) this._size = [0, 0]; | |
this._size[lengthDim] = length - this.options.itemSpacing; // account for last itemSpacing | |
this._size[girthDim] = girth; | |
this._outputCache.size = this.getSize(); | |
return this._outputCache; | |
}; | |
module.exports = SequentialLayout; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/Deck',['require','exports','module','famous/core/Transform','famous/core/OptionsManager','famous/transitions/Transitionable','famous/utilities/Utility','./SequentialLayout'],function(require, exports, module) { | |
var Transform = require('famous/core/Transform'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var Utility = require('famous/utilities/Utility'); | |
var SequentialLayout = require('./SequentialLayout'); | |
/** | |
* A Sequential Layout that can be opened and closed with animations. | |
* | |
* Takes the same options as SequentialLayout | |
* as well as options for the open/close transition | |
* and the rotation you want your Deck instance to layout in. | |
* | |
* @class Deck | |
* @constructor | |
* @extends SequentialLayout | |
* | |
* @param {Options} [options] An object of configurable options | |
* @param {Transition} [options.transition={duration: 500, curve: 'easeOutBounce'} | |
* The transition that executes upon opening or closing your deck instance. | |
* @param {Number} [stackRotation=0] The amount of rotation applied to the propogation | |
* of the Deck instance's stack of renderables. | |
* @param {Object} [options.transition] A transition object for changing between states. | |
* @param {Number} [options.direction] axis of expansion (Utility.Direction.X or .Y) | |
*/ | |
function Deck(options) { | |
SequentialLayout.apply(this, arguments); | |
this.state = new Transitionable(0); | |
this._isOpen = false; | |
this.setOutputFunction(function(input, offset, index) { | |
var state = _getState.call(this); | |
var positionMatrix = (this.options.direction === Utility.Direction.X) ? | |
Transform.translate(state * offset, 0, 0.001 * (state - 1) * offset) : | |
Transform.translate(0, state * offset, 0.001 * (state - 1) * offset); | |
var output = input.render(); | |
if (this.options.stackRotation) { | |
var amount = this.options.stackRotation * index * (1 - state); | |
output = { | |
transform: Transform.rotateZ(amount), | |
origin: [0.5, 0.5], | |
target: output | |
}; | |
} | |
return { | |
transform: positionMatrix, | |
size: input.getSize(), | |
target: output | |
}; | |
}); | |
} | |
Deck.prototype = Object.create(SequentialLayout.prototype); | |
Deck.prototype.constructor = Deck; | |
Deck.DEFAULT_OPTIONS = OptionsManager.patch(SequentialLayout.DEFAULT_OPTIONS, { | |
transition: { | |
curve: 'easeOutBounce', | |
duration: 500 | |
}, | |
stackRotation: 0 | |
}); | |
/** | |
* Returns the width and the height of the Deck instance. | |
* | |
* @method getSize | |
* @return {Array} A two value array of Deck's current width and height (in that order). | |
* Scales as Deck opens and closes. | |
*/ | |
Deck.prototype.getSize = function getSize() { | |
var originalSize = SequentialLayout.prototype.getSize.apply(this, arguments); | |
var firstSize = this._items ? this._items.get().getSize() : [0, 0]; | |
if (!firstSize) firstSize = [0, 0]; | |
var state = _getState.call(this); | |
var invState = 1 - state; | |
return [firstSize[0] * invState + originalSize[0] * state, firstSize[1] * invState + originalSize[1] * state]; | |
}; | |
function _getState(returnFinal) { | |
if (returnFinal) return this._isOpen ? 1 : 0; | |
else return this.state.get(); | |
} | |
function _setState(pos, transition, callback) { | |
this.state.halt(); | |
this.state.set(pos, transition, callback); | |
} | |
/** | |
* An accesor method to find out if the messaged Deck instance is open or closed. | |
* | |
* @method isOpen | |
* @return {Boolean} Returns true if the instance is open or false if it's closed. | |
*/ | |
Deck.prototype.isOpen = function isOpen() { | |
return this._isOpen; | |
}; | |
/** | |
* Sets the Deck instance to an open state. | |
* | |
* @method open | |
* @param {function} [callback] Executes after transitioning to a fully open state. | |
*/ | |
Deck.prototype.open = function open(callback) { | |
this._isOpen = true; | |
_setState.call(this, 1, this.options.transition, callback); | |
}; | |
/** | |
* Sets the Deck instance to an open state. | |
* | |
* @method close | |
* @param {function} [callback] Executes after transitioning to a fully closed state. | |
*/ | |
Deck.prototype.close = function close(callback) { | |
this._isOpen = false; | |
_setState.call(this, 0, this.options.transition, callback); | |
}; | |
/** | |
* Sets the Deck instance from its current state to the opposite state. | |
* | |
* @method close | |
* @param {function} [callback] Executes after transitioning to the toggled state. | |
*/ | |
Deck.prototype.toggle = function toggle(callback) { | |
if (this._isOpen) this.close(callback); | |
else this.open(callback); | |
}; | |
module.exports = Deck; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/RenderController',['require','exports','module','famous/core/Modifier','famous/core/RenderNode','famous/core/Transform','famous/transitions/Transitionable','famous/core/View'],function(require, exports, module) { | |
var Modifier = require('famous/core/Modifier'); | |
var RenderNode = require('famous/core/RenderNode'); | |
var Transform = require('famous/core/Transform'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var View = require('famous/core/View'); | |
/** | |
* A dynamic view that can show or hide different renerables with transitions. | |
* @class RenderController | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Transition} [inTransition=true] The transition in charge of showing a renderable. | |
* @param {Transition} [outTransition=true] The transition in charge of removing your previous renderable when | |
* you show a new one, or hiding your current renderable. | |
* @param {Boolean} [overlap=true] When showing a new renderable, overlap determines if the | |
out transition of the old one executes concurrently with the in transition of the new one, | |
or synchronously beforehand. | |
*/ | |
function RenderController(options) { | |
View.apply(this, arguments); | |
this._showing = -1; | |
this._outgoingRenderables = []; | |
this._nextRenderable = null; | |
this._renderables = []; | |
this._nodes = []; | |
this._modifiers = []; | |
this._states = []; | |
this.inTransformMap = RenderController.DefaultMap.transform; | |
this.inOpacityMap = RenderController.DefaultMap.opacity; | |
this.inOriginMap = RenderController.DefaultMap.origin; | |
this.outTransformMap = RenderController.DefaultMap.transform; | |
this.outOpacityMap = RenderController.DefaultMap.opacity; | |
this.outOriginMap = RenderController.DefaultMap.origin; | |
this._output = []; | |
} | |
RenderController.prototype = Object.create(View.prototype); | |
RenderController.prototype.constructor = RenderController; | |
RenderController.DEFAULT_OPTIONS = { | |
inTransition: true, | |
outTransition: true, | |
overlap: true | |
}; | |
RenderController.DefaultMap = { | |
transform: function() { | |
return Transform.identity; | |
}, | |
opacity: function(progress) { | |
return progress; | |
}, | |
origin: null | |
}; | |
function _mappedState(map, state) { | |
return map(state.get()); | |
} | |
/** | |
* As your RenderController shows a new renderable, it executes a transition in. This transition in | |
* will affect a default interior state and modify it as you bring renderables in and out. However, if you want to control | |
* the transform, opacity, and origin state yourself, you may call certain methods (such as inTransformFrom) to obtain state from an outside source, | |
* that may either be a function or a Famous transitionable. inTransformFrom sets the accessor for the state of | |
* the transform used in transitioning in renderables. | |
* | |
* @method inTransformFrom | |
* @param {Function|Transitionable} transform A function that returns a transform from outside closure, or a | |
* a transitionable that manages a full transform (a sixteen value array). | |
* @chainable | |
*/ | |
RenderController.prototype.inTransformFrom = function inTransformFrom(transform) { | |
if (transform instanceof Function) this.inTransformMap = transform; | |
else if (transform && transform.get) this.inTransformMap = transform.get.bind(transform); | |
else throw new Error('inTransformFrom takes only function or getter object'); | |
//TODO: tween transition | |
return this; | |
}; | |
/** | |
* inOpacityFrom sets the accessor for the state of the opacity used in transitioning in renderables. | |
* @method inOpacityFrom | |
* @param {Function|Transitionable} opacity A function that returns an opacity from outside closure, or a | |
* a transitionable that manages opacity (a number between zero and one). | |
* @chainable | |
*/ | |
RenderController.prototype.inOpacityFrom = function inOpacityFrom(opacity) { | |
if (opacity instanceof Function) this.inOpacityMap = opacity; | |
else if (opacity && opacity.get) this.inOpacityMap = opacity.get.bind(opacity); | |
else throw new Error('inOpacityFrom takes only function or getter object'); | |
//TODO: tween opacity | |
return this; | |
}; | |
/** | |
* inOriginFrom sets the accessor for the state of the origin used in transitioning in renderables. | |
* @method inOriginFrom | |
* @param {Function|Transitionable} origin A function that returns an origin from outside closure, or a | |
* a transitionable that manages origin (a two value array of numbers between zero and one). | |
* @chainable | |
*/ | |
RenderController.prototype.inOriginFrom = function inOriginFrom(origin) { | |
if (origin instanceof Function) this.inOriginMap = origin; | |
else if (origin && origin.get) this.inOriginMap = origin.get.bind(origin); | |
else throw new Error('inOriginFrom takes only function or getter object'); | |
//TODO: tween origin | |
return this; | |
}; | |
/** | |
* outTransformFrom sets the accessor for the state of the transform used in transitioning out renderables. | |
* @method show | |
* @param {Function|Transitionable} transform A function that returns a transform from outside closure, or a | |
* a transitionable that manages a full transform (a sixteen value array). | |
* @chainable | |
*/ | |
RenderController.prototype.outTransformFrom = function outTransformFrom(transform) { | |
if (transform instanceof Function) this.outTransformMap = transform; | |
else if (transform && transform.get) this.outTransformMap = transform.get.bind(transform); | |
else throw new Error('inTransformFrom takes only function or getter object'); | |
//TODO: tween transition | |
return this; | |
}; | |
/** | |
* outOpacityFrom sets the accessor for the state of the opacity used in transitioning out renderables. | |
* @method inOpacityFrom | |
* @param {Function|Transitionable} opacity A function that returns an opacity from outside closure, or a | |
* a transitionable that manages opacity (a number between zero and one). | |
* @chainable | |
*/ | |
RenderController.prototype.outOpacityFrom = function outOpacityFrom(opacity) { | |
if (opacity instanceof Function) this.outOpacityMap = opacity; | |
else if (opacity && opacity.get) this.outOpacityMap = opacity.get.bind(opacity); | |
else throw new Error('inOpacityFrom takes only function or getter object'); | |
//TODO: tween opacity | |
return this; | |
}; | |
/** | |
* outOriginFrom sets the accessor for the state of the origin used in transitioning out renderables. | |
* @method inOriginFrom | |
* @param {Function|Transitionable} origin A function that returns an origin from outside closure, or a | |
* a transitionable that manages origin (a two value array of numbers between zero and one). | |
* @chainable | |
*/ | |
RenderController.prototype.outOriginFrom = function outOriginFrom(origin) { | |
if (origin instanceof Function) this.outOriginMap = origin; | |
else if (origin && origin.get) this.outOriginMap = origin.get.bind(origin); | |
else throw new Error('inOriginFrom takes only function or getter object'); | |
//TODO: tween origin | |
return this; | |
}; | |
/** | |
* Show displays the targeted renderable with a transition and an optional callback to | |
* execute afterwards. | |
* @method show | |
* @param {Object} renderable The renderable you want to show. | |
* @param {Transition} [transition] Overwrites the default transition in to display the | |
* passed-in renderable. | |
* @param {function} [callback] Executes after transitioning in the renderable. | |
* @chainable | |
*/ | |
RenderController.prototype.show = function show(renderable, transition, callback) { | |
if (!renderable) { | |
return this.hide(callback); | |
} | |
if (transition instanceof Function) { | |
callback = transition; | |
transition = null; | |
} | |
if (this._showing >= 0) { | |
if (this.options.overlap) this.hide(); | |
else { | |
if (this._nextRenderable) { | |
this._nextRenderable = renderable; | |
} | |
else { | |
this._nextRenderable = renderable; | |
this.hide(function() { | |
if (this._nextRenderable === renderable) this.show(this._nextRenderable, callback); | |
this._nextRenderable = null; | |
}); | |
} | |
return undefined; | |
} | |
} | |
var state = null; | |
// check to see if we should restore | |
var renderableIndex = this._renderables.indexOf(renderable); | |
if (renderableIndex >= 0) { | |
this._showing = renderableIndex; | |
state = this._states[renderableIndex]; | |
state.halt(); | |
var outgoingIndex = this._outgoingRenderables.indexOf(renderable); | |
if (outgoingIndex >= 0) this._outgoingRenderables.splice(outgoingIndex, 1); | |
} | |
else { | |
state = new Transitionable(0); | |
var modifier = new Modifier({ | |
transform: this.inTransformMap ? _mappedState.bind(this, this.inTransformMap, state) : null, | |
opacity: this.inOpacityMap ? _mappedState.bind(this, this.inOpacityMap, state) : null, | |
origin: this.inOriginMap ? _mappedState.bind(this, this.inOriginMap, state) : null | |
}); | |
var node = new RenderNode(); | |
node.add(modifier).add(renderable); | |
this._showing = this._nodes.length; | |
this._nodes.push(node); | |
this._modifiers.push(modifier); | |
this._states.push(state); | |
this._renderables.push(renderable); | |
} | |
if (!transition) transition = this.options.inTransition; | |
state.set(1, transition, callback); | |
}; | |
/** | |
* Hide hides the currently displayed renderable with an out transition. | |
* @method hide | |
* @param {Transition} [transition] Overwrites the default transition in to hide the | |
* currently controlled renderable. | |
* @param {function} [callback] Executes after transitioning out the renderable. | |
* @chainable | |
*/ | |
RenderController.prototype.hide = function hide(transition, callback) { | |
if (this._showing < 0) return; | |
var index = this._showing; | |
this._showing = -1; | |
if (transition instanceof Function) { | |
callback = transition; | |
transition = undefined; | |
} | |
var node = this._nodes[index]; | |
var modifier = this._modifiers[index]; | |
var state = this._states[index]; | |
var renderable = this._renderables[index]; | |
modifier.transformFrom(this.outTransformMap ? _mappedState.bind(this, this.outTransformMap, state) : null); | |
modifier.opacityFrom(this.outOpacityMap ? _mappedState.bind(this, this.outOpacityMap, state) : null); | |
modifier.originFrom(this.outOriginMap ? _mappedState.bind(this, this.outOriginMap, state) : null); | |
if (this._outgoingRenderables.indexOf(renderable) < 0) this._outgoingRenderables.push(renderable); | |
if (!transition) transition = this.options.outTransition; | |
state.halt(); | |
state.set(0, transition, function(node, modifier, state, renderable) { | |
if (this._outgoingRenderables.indexOf(renderable) >= 0) { | |
var index = this._nodes.indexOf(node); | |
this._nodes.splice(index, 1); | |
this._modifiers.splice(index, 1); | |
this._states.splice(index, 1); | |
this._renderables.splice(index, 1); | |
this._outgoingRenderables.splice(this._outgoingRenderables.indexOf(renderable), 1); | |
if (this._showing >= index) this._showing--; | |
} | |
if (callback) callback.call(this); | |
}.bind(this, node, modifier, state, renderable)); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
RenderController.prototype.render = function render() { | |
var result = this._output; | |
if (result.length > this._nodes.length) result.splice(this._nodes.length); | |
for (var i = 0; i < this._nodes.length; i++) { | |
result[i] = this._nodes[i].render(); | |
} | |
return result; | |
}; | |
module.exports = RenderController; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/EdgeSwapper',['require','exports','module','famous/transitions/CachedMap','famous/core/Entity','famous/core/EventHandler','famous/core/Transform','./RenderController'],function(require, exports, module) { | |
var CachedMap = require('famous/transitions/CachedMap'); | |
var Entity = require('famous/core/Entity'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Transform = require('famous/core/Transform'); | |
var RenderController = require('./RenderController'); | |
/** | |
* Container which handles swapping renderables from the edge of its parent context. | |
* @class EdgeSwapper | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* Takes the same options as RenderController. | |
* @uses RenderController | |
*/ | |
function EdgeSwapper(options) { | |
this._currentTarget = null; | |
this._size = [undefined, undefined]; | |
this._controller = new RenderController(options); | |
this._controller.inTransformFrom(CachedMap.create(_transformMap.bind(this, 0.0001))); | |
this._controller.outTransformFrom(CachedMap.create(_transformMap.bind(this, -0.0001))); | |
this._eventInput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
this._entityId = Entity.register(this); | |
if (options) this.setOptions(options); | |
} | |
function _transformMap(zMax, progress) { | |
return Transform.translate(this._size[0] * (1 - progress), 0, zMax * (1 - progress)); | |
} | |
/** | |
* Displays the passed-in content with the EdgeSwapper instance's default transition. | |
* | |
* @method show | |
* @param {Object} content The renderable you want to display. | |
*/ | |
EdgeSwapper.prototype.show = function show(content) { | |
// stop sending input to old target | |
if (this._currentTarget) this._eventInput.unpipe(this._currentTarget); | |
this._currentTarget = content; | |
// start sending input to new target | |
if (this._currentTarget && this._currentTarget.trigger) this._eventInput.pipe(this._currentTarget); | |
this._controller.show.apply(this._controller, arguments); | |
}; | |
/** | |
* Patches the EdgeSwapper instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the Edgeswapper instance. | |
*/ | |
EdgeSwapper.prototype.setOptions = function setOptions(options) { | |
this._controller.setOptions(options); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
EdgeSwapper.prototype.render = function render() { | |
return this._entityId; | |
}; | |
/** | |
* 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 | |
*/ | |
EdgeSwapper.prototype.commit = function commit(context) { | |
this._size[0] = context.size[0]; | |
this._size[1] = context.size[1]; | |
return { | |
transform: context.transform, | |
opacity: context.opacity, | |
origin: context.origin, | |
size: context.size, | |
target: this._controller.render() | |
}; | |
}; | |
module.exports = EdgeSwapper; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/FlexibleLayout',['require','exports','module','famous/core/Entity','famous/core/Transform','famous/core/OptionsManager','famous/core/EventHandler','famous/transitions/Transitionable'],function(require, exports, module) { | |
var Entity = require('famous/core/Entity'); | |
var Transform = require('famous/core/Transform'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
/** | |
* A layout which divides a context into sections based on a proportion | |
* of the total sum of ratios. FlexibleLayout can either lay renderables | |
* out vertically or horizontally. | |
* @class FlexibleLayout | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.direction=0] Direction the FlexibleLayout instance should lay out renderables. | |
* @param {Transition} [options.transition=false] The transiton that controls the FlexibleLayout instance's reflow. | |
* @param {Ratios} [options.ratios=[]] The proportions for the renderables to maintain | |
*/ | |
function FlexibleLayout(options) { | |
this.options = Object.create(FlexibleLayout.DEFAULT_OPTIONS); | |
this.optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this.id = Entity.register(this); | |
this._ratios = new Transitionable(this.options.ratios); | |
this._nodes = []; | |
this._cachedDirection = null; | |
this._cachedTotalLength = false; | |
this._cachedLengths = []; | |
this._cachedTransforms = null; | |
this._ratiosDirty = false; | |
this._eventOutput = new EventHandler(); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
FlexibleLayout.DIRECTION_X = 0; | |
FlexibleLayout.DIRECTION_Y = 1; | |
FlexibleLayout.DEFAULT_OPTIONS = { | |
direction: FlexibleLayout.DIRECTION_X, | |
transition: false, | |
ratios : [] | |
}; | |
function _reflow(ratios, length, direction) { | |
var currTransform; | |
var translation = 0; | |
var flexLength = length; | |
var ratioSum = 0; | |
var ratio; | |
var node; | |
var i; | |
this._cachedLengths = []; | |
this._cachedTransforms = []; | |
for (i = 0; i < ratios.length; i++){ | |
ratio = ratios[i]; | |
node = this._nodes[i]; | |
if (typeof ratio !== 'number') | |
flexLength -= node.getSize()[direction] || 0; | |
else | |
ratioSum += ratio; | |
} | |
for (i = 0; i < ratios.length; i++) { | |
node = this._nodes[i]; | |
ratio = ratios[i]; | |
length = (typeof ratio === 'number') | |
? flexLength * ratio / ratioSum | |
: node.getSize()[direction]; | |
currTransform = (direction === FlexibleLayout.DIRECTION_X) | |
? Transform.translate(translation, 0, 0) | |
: Transform.translate(0, translation, 0); | |
this._cachedTransforms.push(currTransform); | |
this._cachedLengths.push(length); | |
translation += length; | |
} | |
} | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {Object} Render spec for this component | |
*/ | |
FlexibleLayout.prototype.render = function render() { | |
return this.id; | |
}; | |
/** | |
* Patches the FlexibleLayouts instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the FlexibleLayout instance. | |
*/ | |
FlexibleLayout.prototype.setOptions = function setOptions(options) { | |
this.optionsManager.setOptions(options); | |
}; | |
/** | |
* Sets the collection of renderables under the FlexibleLayout instance's control. Also sets | |
* the associated ratio values for sizing the renderables if given. | |
* | |
* @method sequenceFrom | |
* @param {Array} sequence An array of renderables. | |
*/ | |
FlexibleLayout.prototype.sequenceFrom = function sequenceFrom(sequence) { | |
this._nodes = sequence; | |
if (this._ratios.get().length === 0) { | |
var ratios = []; | |
for (var i = 0; i < this._nodes.length; i++) ratios.push(1); | |
this.setRatios(ratios); | |
} | |
}; | |
/** | |
* Sets the associated ratio values for sizing the renderables. | |
* | |
* @method setRatios | |
* @param {Array} ratios Array of ratios corresponding to the percentage sizes each renderable should be | |
*/ | |
FlexibleLayout.prototype.setRatios = function setRatios(ratios, transition, callback) { | |
if (transition === undefined) transition = this.options.transition; | |
var currRatios = this._ratios; | |
if (currRatios.get().length === 0) transition = undefined; | |
if (currRatios.isActive()) currRatios.halt(); | |
currRatios.set(ratios, transition, callback); | |
this._ratiosDirty = 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 | |
*/ | |
FlexibleLayout.prototype.commit = function commit(context) { | |
var parentSize = context.size; | |
var parentTransform = context.transform; | |
var parentOrigin = context.origin; | |
var ratios = this._ratios.get(); | |
var direction = this.options.direction; | |
var length = parentSize[direction]; | |
var size; | |
if (length !== this._cachedTotalLength || this._ratiosDirty || this._ratios.isActive() || direction !== this._cachedDirection) { | |
_reflow.call(this, ratios, length, direction); | |
if (length !== this._cachedTotalLength) this._cachedTotalLength = length; | |
if (direction !== this._cachedDirection) this._cachedDirection = direction; | |
if (this._ratiosDirty) this._ratiosDirty = false; | |
} | |
var result = []; | |
for (var i = 0; i < ratios.length; i++) { | |
size = [undefined, undefined]; | |
length = this._cachedLengths[i]; | |
size[direction] = length; | |
result.push({ | |
transform : this._cachedTransforms[i], | |
size: size, | |
target : this._nodes[i].render() | |
}); | |
} | |
if (parentSize && (parentOrigin[0] !== 0 && parentOrigin[1] !== 0)) | |
parentTransform = Transform.moveThen([-parentSize[0]*parentOrigin[0], -parentSize[1]*parentOrigin[1], 0], parentTransform); | |
return { | |
transform: parentTransform, | |
size: parentSize, | |
target: result | |
}; | |
}; | |
module.exports = FlexibleLayout; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/Flipper',['require','exports','module','famous/core/Transform','famous/transitions/Transitionable','famous/core/RenderNode','famous/core/OptionsManager'],function(require, exports, module) { | |
var Transform = require('famous/core/Transform'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var RenderNode = require('famous/core/RenderNode'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* Allows you to link two renderables as front and back sides that can be | |
* 'flipped' back and forth along a chosen axis. Rendering optimizations are | |
* automatically handled. | |
* | |
* @class Flipper | |
* @constructor | |
* @param {Options} [options] An object of options. | |
* @param {Transition} [options.transition=true] The transition executed when flipping your Flipper instance. | |
*/ | |
function Flipper(options) { | |
this.options = Object.create(Flipper.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this.angle = new Transitionable(0); | |
this.frontNode = undefined; | |
this.backNode = undefined; | |
this.flipped = false; | |
} | |
Flipper.DIRECTION_X = 0; | |
Flipper.DIRECTION_Y = 1; | |
var SEPERATION_LENGTH = 1; | |
Flipper.DEFAULT_OPTIONS = { | |
transition: true, | |
direction: Flipper.DIRECTION_X | |
}; | |
/** | |
* Toggles the rotation between the front and back renderables | |
* | |
* @method flip | |
* @param {Object} [transition] Transition definition | |
* @param {Function} [callback] Callback | |
*/ | |
Flipper.prototype.flip = function flip(transition, callback) { | |
var angle = this.flipped ? 0 : Math.PI; | |
this.setAngle(angle, transition, callback); | |
this.flipped = !this.flipped; | |
}; | |
/** | |
* Basic setter to the angle | |
* | |
* @method setAngle | |
* @param {Number} angle | |
* @param {Object} [transition] Transition definition | |
* @param {Function} [callback] Callback | |
*/ | |
Flipper.prototype.setAngle = function setAngle(angle, transition, callback) { | |
if (transition === undefined) transition = this.options.transition; | |
if (this.angle.isActive()) this.angle.halt(); | |
this.angle.set(angle, transition, callback); | |
}; | |
/** | |
* Patches the Flipper instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the Flipper instance. | |
*/ | |
Flipper.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
/** | |
* Adds the passed-in renderable to the view associated with the 'front' of the Flipper instance. | |
* | |
* @method setFront | |
* @chainable | |
* @param {Object} node The renderable you want to add to the front. | |
*/ | |
Flipper.prototype.setFront = function setFront(node) { | |
this.frontNode = node; | |
}; | |
/** | |
* Adds the passed-in renderable to the view associated with the 'back' of the Flipper instance. | |
* | |
* @method setBack | |
* @chainable | |
* @param {Object} node The renderable you want to add to the back. | |
*/ | |
Flipper.prototype.setBack = function setBack(node) { | |
this.backNode = node; | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {Number} Render spec for this component | |
*/ | |
Flipper.prototype.render = function render() { | |
var angle = this.angle.get(); | |
var frontTransform; | |
var backTransform; | |
if (this.options.direction === Flipper.DIRECTION_X) { | |
frontTransform = Transform.rotateY(angle); | |
backTransform = Transform.rotateY(angle + Math.PI); | |
} | |
else { | |
frontTransform = Transform.rotateX(angle); | |
backTransform = Transform.rotateX(angle + Math.PI); | |
} | |
var result = []; | |
if (this.frontNode){ | |
result.push({ | |
transform: frontTransform, | |
target: this.frontNode.render() | |
}); | |
} | |
if (this.backNode){ | |
result.push({ | |
transform: Transform.moveThen([0, 0, SEPERATION_LENGTH], backTransform), | |
target: this.backNode.render() | |
}); | |
} | |
return result; | |
}; | |
module.exports = Flipper; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/GridLayout',['require','exports','module','famous/core/Entity','famous/core/RenderNode','famous/core/Transform','famous/core/ViewSequence','famous/core/EventHandler','famous/core/Modifier','famous/core/OptionsManager','famous/transitions/Transitionable','famous/transitions/TransitionableTransform'],function(require, exports, module) { | |
var Entity = require('famous/core/Entity'); | |
var RenderNode = require('famous/core/RenderNode'); | |
var Transform = require('famous/core/Transform'); | |
var ViewSequence = require('famous/core/ViewSequence'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Modifier = require('famous/core/Modifier'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var TransitionableTransform = require('famous/transitions/TransitionableTransform'); | |
/** | |
* A layout which divides a context into several evenly-sized grid cells. | |
* If dimensions are provided, the grid is evenly subdivided with children | |
* cells representing their own context, otherwise the cellSize property is used to compute | |
* dimensions so that items of cellSize will fit. | |
* @class GridLayout | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Array.Number} [options.dimensions=[1, 1]] A two value array which specifies the amount of columns | |
* and rows in your Gridlayout instance. | |
* @param {Array.Number} [options.cellSize=[250, 250]] A two-value array which specifies the width and height | |
* of each cell in your Gridlayout instance. | |
* @param {Transition} [options.transition=false] The transiton that controls the Gridlayout instance's reflow. | |
*/ | |
function GridLayout(options) { | |
this.options = Object.create(GridLayout.DEFAULT_OPTIONS); | |
this.optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this.id = Entity.register(this); | |
this._modifiers = []; | |
this._states = []; | |
this._contextSizeCache = [0, 0]; | |
this._dimensionsCache = [0, 0]; | |
this._activeCount = 0; | |
this._eventOutput = new EventHandler(); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
function _reflow(size, cols, rows) { | |
var usableSize = [size[0], size[1]]; | |
usableSize[0] -= this.options.gutterSize[0] * (cols - 1); | |
usableSize[1] -= this.options.gutterSize[1] * (rows - 1); | |
var rowSize = Math.round(usableSize[1] / rows); | |
var colSize = Math.round(usableSize[0] / cols); | |
var currY = 0; | |
var currX; | |
var currIndex = 0; | |
for (var i = 0; i < rows; i++) { | |
currX = 0; | |
for (var j = 0; j < cols; j++) { | |
if (this._modifiers[currIndex] === undefined) { | |
_createModifier.call(this, currIndex, [colSize, rowSize], [currX, currY, 0], 1); | |
} | |
else { | |
_animateModifier.call(this, currIndex, [colSize, rowSize], [currX, currY, 0], 1); | |
} | |
currIndex++; | |
currX += colSize + this.options.gutterSize[0]; | |
} | |
currY += rowSize + this.options.gutterSize[1]; | |
} | |
this._dimensionsCache = [this.options.dimensions[0], this.options.dimensions[1]]; | |
this._contextSizeCache = [size[0], size[1]]; | |
this._activeCount = rows * cols; | |
for (i = this._activeCount ; i < this._modifiers.length; i++) _animateModifier.call(this, i, [Math.round(colSize), Math.round(rowSize)], [0, 0], 0); | |
this._eventOutput.emit('reflow'); | |
} | |
function _createModifier(index, size, position, opacity) { | |
var transitionItem = { | |
transform: new TransitionableTransform(Transform.translate.apply(null, position)), | |
opacity: new Transitionable(opacity), | |
size: new Transitionable(size) | |
}; | |
var modifier = new Modifier({ | |
transform: transitionItem.transform, | |
opacity: transitionItem.opacity, | |
size: transitionItem.size | |
}); | |
this._states[index] = transitionItem; | |
this._modifiers[index] = modifier; | |
} | |
function _animateModifier(index, size, position, opacity) { | |
var currState = this._states[index]; | |
var currSize = currState.size; | |
var currOpacity = currState.opacity; | |
var currTransform = currState.transform; | |
var transition = this.options.transition; | |
currTransform.halt(); | |
currOpacity.halt(); | |
currSize.halt(); | |
currTransform.setTranslate(position, transition); | |
currSize.set(size, transition); | |
currOpacity.set(opacity, transition); | |
} | |
GridLayout.DEFAULT_OPTIONS = { | |
dimensions: [1, 1], | |
transition: false, | |
gutterSize: [0, 0] | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {Object} Render spec for this component | |
*/ | |
GridLayout.prototype.render = function render() { | |
return this.id; | |
}; | |
/** | |
* Patches the GridLayout instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the GridLayout instance. | |
*/ | |
GridLayout.prototype.setOptions = function setOptions(options) { | |
return this.optionsManager.setOptions(options); | |
}; | |
/** | |
* Sets the collection of renderables under the Gridlayout instance's control. | |
* | |
* @method sequenceFrom | |
* @param {Array|ViewSequence} sequence Either an array of renderables or a Famous viewSequence. | |
*/ | |
GridLayout.prototype.sequenceFrom = function sequenceFrom(sequence) { | |
if (sequence instanceof Array) sequence = new ViewSequence(sequence); | |
this.sequence = sequence; | |
}; | |
/** | |
* 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 | |
*/ | |
GridLayout.prototype.commit = function commit(context) { | |
var transform = context.transform; | |
var opacity = context.opacity; | |
var origin = context.origin; | |
var size = context.size; | |
var cols = this.options.dimensions[0]; | |
var rows = this.options.dimensions[1]; | |
if (size[0] !== this._contextSizeCache[0] || size[1] !== this._contextSizeCache[1] || cols !== this._dimensionsCache[0] || rows !== this._dimensionsCache[1]) { | |
_reflow.call(this, size, cols, rows); | |
} | |
var sequence = this.sequence; | |
var result = []; | |
var currIndex = 0; | |
while (sequence && (currIndex < this._modifiers.length)) { | |
var item = sequence.get(); | |
var modifier = this._modifiers[currIndex]; | |
if (currIndex >= this._activeCount && this._states[currIndex].opacity.isActive()) { | |
this._modifiers.splice(currIndex, 1); | |
this._states.splice(currIndex, 1); | |
} | |
if (item) { | |
result.push( | |
modifier.modify({ | |
origin: origin, | |
target: item.render() | |
}) | |
); | |
} | |
sequence = sequence.getNext(); | |
currIndex++; | |
} | |
if (size) transform = Transform.moveThen([-size[0]*origin[0], -size[1]*origin[1], 0], transform); | |
return { | |
transform: transform, | |
opacity: opacity, | |
size: size, | |
target: result | |
}; | |
}; | |
module.exports = GridLayout; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/HeaderFooterLayout',['require','exports','module','famous/core/Entity','famous/core/RenderNode','famous/core/Transform','famous/core/OptionsManager'],function(require, exports, module) { | |
var Entity = require('famous/core/Entity'); | |
var RenderNode = require('famous/core/RenderNode'); | |
var Transform = require('famous/core/Transform'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* A layout which will arrange three renderables into a header and footer area of defined size, | |
and a content area of flexible size. | |
* @class HeaderFooterLayout | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.direction=HeaderFooterLayout.DIRECTION_Y] A direction of HeaderFooterLayout.DIRECTION_X | |
* lays your HeaderFooterLayout instance horizontally, and a direction of HeaderFooterLayout.DIRECTION_Y | |
* lays it out vertically. | |
* @param {Number} [options.headerSize=undefined] The amount of pixels allocated to the header node | |
* in the HeaderFooterLayout instance's direction. | |
* @param {Number} [options.footerSize=undefined] The amount of pixels allocated to the footer node | |
* in the HeaderFooterLayout instance's direction. | |
*/ | |
function HeaderFooterLayout(options) { | |
this.options = Object.create(HeaderFooterLayout.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this._entityId = Entity.register(this); | |
this.header = new RenderNode(); | |
this.footer = new RenderNode(); | |
this.content = new RenderNode(); | |
} | |
/** | |
* When used as a value for your HeaderFooterLayout's direction option, causes it to lay out horizontally. | |
* | |
* @attribute DIRECTION_X | |
* @type Number | |
* @static | |
* @default 0 | |
* @protected | |
*/ | |
HeaderFooterLayout.DIRECTION_X = 0; | |
/** | |
* When used as a value for your HeaderFooterLayout's direction option, causes it to lay out vertically. | |
* | |
* @attribute DIRECTION_Y | |
* @type Number | |
* @static | |
* @default 1 | |
* @protected | |
*/ | |
HeaderFooterLayout.DIRECTION_Y = 1; | |
HeaderFooterLayout.DEFAULT_OPTIONS = { | |
direction: HeaderFooterLayout.DIRECTION_Y, | |
headerSize: undefined, | |
footerSize: undefined, | |
defaultHeaderSize: 0, | |
defaultFooterSize: 0 | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {Object} Render spec for this component | |
*/ | |
HeaderFooterLayout.prototype.render = function render() { | |
return this._entityId; | |
}; | |
/** | |
* Patches the HeaderFooterLayout instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the HeaderFooterLayout instance. | |
*/ | |
HeaderFooterLayout.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
function _resolveNodeSize(node, defaultSize) { | |
var nodeSize = node.getSize(); | |
return nodeSize ? nodeSize[this.options.direction] : defaultSize; | |
} | |
function _outputTransform(offset) { | |
if (this.options.direction === HeaderFooterLayout.DIRECTION_X) return Transform.translate(offset, 0, 0); | |
else return Transform.translate(0, offset, 0); | |
} | |
function _finalSize(directionSize, size) { | |
if (this.options.direction === HeaderFooterLayout.DIRECTION_X) return [directionSize, size[1]]; | |
else return [size[0], directionSize]; | |
} | |
/** | |
* 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 | |
*/ | |
HeaderFooterLayout.prototype.commit = function commit(context) { | |
var transform = context.transform; | |
var origin = context.origin; | |
var size = context.size; | |
var opacity = context.opacity; | |
var headerSize = (this.options.headerSize !== undefined) ? this.options.headerSize : _resolveNodeSize.call(this, this.header, this.options.defaultHeaderSize); | |
var footerSize = (this.options.footerSize !== undefined) ? this.options.footerSize : _resolveNodeSize.call(this, this.footer, this.options.defaultFooterSize); | |
var contentSize = size[this.options.direction] - headerSize - footerSize; | |
if (size) transform = Transform.moveThen([-size[0]*origin[0], -size[1]*origin[1], 0], transform); | |
var result = [ | |
{ | |
size: _finalSize.call(this, headerSize, size), | |
target: this.header.render() | |
}, | |
{ | |
transform: _outputTransform.call(this, headerSize), | |
size: _finalSize.call(this, contentSize, size), | |
target: this.content.render() | |
}, | |
{ | |
transform: _outputTransform.call(this, headerSize + contentSize), | |
size: _finalSize.call(this, footerSize, size), | |
target: this.footer.render() | |
} | |
]; | |
return { | |
transform: transform, | |
opacity: opacity, | |
size: size, | |
target: result | |
}; | |
}; | |
module.exports = HeaderFooterLayout; | |
}); | |
define('famous/views/Lightbox',['require','exports','module','famous/core/Transform','famous/core/Modifier','famous/core/RenderNode','famous/utilities/Utility','famous/core/OptionsManager','famous/transitions/Transitionable','famous/transitions/TransitionableTransform'],function(require, exports, module) { | |
var Transform = require('famous/core/Transform'); | |
var Modifier = require('famous/core/Modifier'); | |
var RenderNode = require('famous/core/RenderNode'); | |
var Utility = require('famous/utilities/Utility'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var Transitionable = require('famous/transitions/Transitionable'); | |
var TransitionableTransform = require('famous/transitions/TransitionableTransform'); | |
/** | |
* Lightbox, using transitions, shows and hides different renderables. Lightbox can essentially be | |
* thought of as RenderController with a stateful implementation and interface. | |
* | |
* @class Lightbox | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Transform} [options.inTransform] The transform at the start of transitioning in a shown renderable. | |
* @param {Transform} [options.outTransform] The transform at the end of transitioning out a renderable. | |
* @param {Transform} [options.showTransform] The transform applied to your shown renderable in its state of equilibrium. | |
* @param {Number} [options.inOpacity] A number between one and zero that defines the state of a shown renderables opacity upon initially | |
* being transitioned in. | |
* @param {Number} [options.outOpacity] A number between one and zero that defines the state of a shown renderables opacity upon being | |
* fully transitioned out. | |
* @param {Number} [options.showOpacity] A number between one and zero that defines the state of a shown renderables opacity | |
* once succesfully transitioned in. | |
* @param {Array<Number>} [options.inOrigin] A two value array of numbers between one and zero that defines the state of a shown renderables | |
* origin upon intially being transitioned in. | |
* @param {Array<Number>} [options.outOrigin] A two value array of numbers between one and zero that defines the state of a shown renderable | |
* once fully hidden. | |
* @param {Array<Number>} [options.showOrigin] A two value array of numbers between one and zero that defines the state of a shown renderables | |
* origin upon succesfully being shown. | |
* @param {Transition} [options.inTransition=true] The transition in charge of showing a renderable. | |
* @param {Transition} [options.outTransition=true] The transition in charge of removing your previous renderable when | |
* you show a new one, or hiding your current renderable. | |
* @param {Boolean} [options.overlap=false] When showing a new renderable, overlap determines if the | |
* out transition of the old one executes concurrently with the in transition of the new one, | |
* or synchronously beforehand. | |
*/ | |
function Lightbox(options) { | |
this.options = Object.create(Lightbox.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this._showing = false; | |
this.nodes = []; | |
this.transforms = []; | |
this.states = []; | |
} | |
Lightbox.DEFAULT_OPTIONS = { | |
inTransform: Transform.scale(0.001, 0.001, 0.001), | |
inOpacity: 0, | |
inOrigin: [0.5, 0.5], | |
outTransform: Transform.scale(0.001, 0.001, 0.001), | |
outOpacity: 0, | |
outOrigin: [0.5, 0.5], | |
showTransform: Transform.identity, | |
showOpacity: 1, | |
showOrigin: [0.5, 0.5], | |
inTransition: true, | |
outTransition: true, | |
overlap: false | |
}; | |
/** | |
* Patches the Lightbox instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the Lightbox instance. | |
*/ | |
Lightbox.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
/** | |
* Show displays the targeted renderable with a transition and an optional callback to | |
* execute afterwards. | |
* @method show | |
* @param {Object} renderable The renderable you want to show. | |
* @param {Transition} [transition] Overwrites the default transition in to display the | |
* passed-in renderable. | |
* @param {function} [callback] Executes after transitioning in the renderable. | |
*/ | |
Lightbox.prototype.show = function show(renderable, transition, callback) { | |
if (!renderable) { | |
return this.hide(callback); | |
} | |
if (transition instanceof Function) { | |
callback = transition; | |
transition = undefined; | |
} | |
if (this._showing) { | |
if (this.options.overlap) this.hide(); | |
else { | |
return this.hide(this.show.bind(this, renderable, transition, callback)); | |
} | |
} | |
this._showing = true; | |
var stateItem = { | |
transform: new TransitionableTransform(this.options.inTransform), | |
origin: new Transitionable(this.options.inOrigin), | |
opacity: new Transitionable(this.options.inOpacity) | |
}; | |
var transform = new Modifier({ | |
transform: stateItem.transform, | |
opacity: stateItem.opacity, | |
origin: stateItem.origin | |
}); | |
var node = new RenderNode(); | |
node.add(transform).add(renderable); | |
this.nodes.push(node); | |
this.states.push(stateItem); | |
this.transforms.push(transform); | |
var _cb = callback ? Utility.after(3, callback) : undefined; | |
if (!transition) transition = this.options.inTransition; | |
stateItem.transform.set(this.options.showTransform, transition, _cb); | |
stateItem.opacity.set(this.options.showOpacity, transition, _cb); | |
stateItem.origin.set(this.options.showOrigin, transition, _cb); | |
}; | |
/** | |
* Hide hides the currently displayed renderable with an out transition. | |
* @method hide | |
* @param {Transition} [transition] Overwrites the default transition in to hide the | |
* currently controlled renderable. | |
* @param {function} [callback] Executes after transitioning out the renderable. | |
*/ | |
Lightbox.prototype.hide = function hide(transition, callback) { | |
if (!this._showing) return; | |
this._showing = false; | |
if (transition instanceof Function) { | |
callback = transition; | |
transition = undefined; | |
} | |
var node = this.nodes[this.nodes.length - 1]; | |
var transform = this.transforms[this.transforms.length - 1]; | |
var stateItem = this.states[this.states.length - 1]; | |
var _cb = Utility.after(3, function() { | |
this.nodes.splice(this.nodes.indexOf(node), 1); | |
this.states.splice(this.states.indexOf(stateItem), 1); | |
this.transforms.splice(this.transforms.indexOf(transform), 1); | |
if (callback) callback.call(this); | |
}.bind(this)); | |
if (!transition) transition = this.options.outTransition; | |
stateItem.transform.set(this.options.outTransform, transition, _cb); | |
stateItem.opacity.set(this.options.outOpacity, transition, _cb); | |
stateItem.origin.set(this.options.outOrigin, transition, _cb); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
Lightbox.prototype.render = function render() { | |
var result = []; | |
for (var i = 0; i < this.nodes.length; i++) { | |
result.push(this.nodes[i].render()); | |
} | |
return result; | |
}; | |
module.exports = Lightbox; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/forces/Drag',['require','exports','module','./Force'],function(require, exports, module) { | |
var Force = require('./Force'); | |
/** | |
* Drag is a force that opposes velocity. Attach it to the physics engine | |
* to slow down a physics body in motion. | |
* | |
* @class Drag | |
* @constructor | |
* @extends Force | |
* @param {Object} options options to set on drag | |
*/ | |
function Drag(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
Force.call(this); | |
} | |
Drag.prototype = Object.create(Force.prototype); | |
Drag.prototype.constructor = Drag; | |
/** | |
* @property Drag.FORCE_FUNCTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Drag.FORCE_FUNCTIONS = { | |
/** | |
* A drag force proportional to the velocity | |
* @attribute LINEAR | |
* @type Function | |
* @param {Vector} velocity | |
* @return {Vector} drag force | |
*/ | |
LINEAR : function(velocity) { | |
return velocity; | |
}, | |
/** | |
* A drag force proportional to the square of the velocity | |
* @attribute QUADRATIC | |
* @type Function | |
* @param {Vector} velocity | |
* @return {Vector} drag force | |
*/ | |
QUADRATIC : function(velocity) { | |
return velocity.mult(velocity.norm()); | |
} | |
}; | |
/** | |
* @property Drag.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Drag.DEFAULT_OPTIONS = { | |
/** | |
* The strength of the force | |
* Range : [0, 0.1] | |
* @attribute strength | |
* @type Number | |
* @default 0.01 | |
*/ | |
strength : 0.01, | |
/** | |
* The type of opposing force | |
* @attribute forceFunction | |
* @type Function | |
*/ | |
forceFunction : Drag.FORCE_FUNCTIONS.LINEAR | |
}; | |
/** | |
* Adds a drag force to a physics body's force accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.Body} Array of bodies to apply drag force to. | |
*/ | |
Drag.prototype.applyForce = function applyForce(targets) { | |
var strength = this.options.strength; | |
var forceFunction = this.options.forceFunction; | |
var force = this.force; | |
for (var index = 0; index < targets.length; index++) { | |
var particle = targets[index]; | |
forceFunction(particle.velocity).mult(-strength).put(force); | |
particle.applyForce(force); | |
} | |
}; | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param {Objects} options | |
*/ | |
Drag.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
module.exports = Drag; | |
}); | |
define('famous/views/Scroller',['require','exports','module','famous/core/Entity','famous/core/Group','famous/core/OptionsManager','famous/core/Transform','famous/utilities/Utility','famous/core/ViewSequence','famous/core/EventHandler'],function(require, exports, module) { | |
var Entity = require('famous/core/Entity'); | |
var Group = require('famous/core/Group'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var Transform = require('famous/core/Transform'); | |
var Utility = require('famous/utilities/Utility'); | |
var ViewSequence = require('famous/core/ViewSequence'); | |
var EventHandler = require('famous/core/EventHandler'); | |
/** | |
* Scroller lays out a collection of renderables, and will browse through them based on | |
* accessed position. Scroller also broadcasts an 'edgeHit' event, with a position property of the location of the edge, | |
* when you've hit the 'edges' of it's renderable collection. | |
* @class Scroller | |
* @constructor | |
* @event error | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.direction=Utility.Direction.Y] Using the direction helper found in the famous Utility | |
* module, this option will lay out the Scroller instance's renderables either horizontally | |
* (x) or vertically (y). Utility's direction is essentially either zero (X) or one (Y), so feel free | |
* to just use integers as well. | |
* @param {Number} [clipSize=undefined] The size of the area (in pixels) that Scroller will display content in. | |
* @param {Number} [margin=undefined] The size of the area (in pixels) that Scroller will process renderables' associated calculations in. | |
*/ | |
function Scroller(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this._optionsManager.setOptions(options); | |
this._node = null; | |
this._position = 0; | |
// used for shifting nodes | |
this._positionOffset = 0; | |
this._positionGetter = null; | |
this._outputFunction = null; | |
this._masterOutputFunction = null; | |
this.outputFrom(); | |
this._onEdge = 0; // -1 for top, 1 for bottom | |
this.group = new Group(); | |
this.group.add({render: _innerRender.bind(this)}); | |
this._entityId = Entity.register(this); | |
this._size = [undefined, undefined]; | |
this._contextSize = [undefined, undefined]; | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
} | |
Scroller.DEFAULT_OPTIONS = { | |
direction: Utility.Direction.Y, | |
margin: 0, | |
clipSize: undefined, | |
groupScroll: false | |
}; | |
function _sizeForDir(size) { | |
if (!size) size = this._contextSize; | |
var dimension = (this.options.direction === Utility.Direction.X) ? 0 : 1; | |
return (size[dimension] === undefined) ? this._contextSize[dimension] : size[dimension]; | |
} | |
function _output(node, offset, target) { | |
var size = node.getSize ? node.getSize() : this._contextSize; | |
var transform = this._outputFunction(offset); | |
target.push({transform: transform, target: node.render()}); | |
return _sizeForDir.call(this, size); | |
} | |
function _getClipSize() { | |
if (this.options.clipSize) return this.options.clipSize; | |
else return _sizeForDir.call(this, this._contextSize); | |
} | |
/** | |
* Patches the Scroller instance's options with the passed-in ones. | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the Scroller instance. | |
*/ | |
Scroller.prototype.setOptions = function setOptions(options) { | |
this._optionsManager.setOptions(options); | |
if (this.options.groupScroll) { | |
this.group.pipe(this._eventOutput); | |
} | |
else { | |
this.group.unpipe(this._eventOutput); | |
} | |
}; | |
/** | |
* Tells you if the Scroller instance is on an edge. | |
* @method onEdge | |
* @return {Boolean} Whether the Scroller instance is on an edge or not. | |
*/ | |
Scroller.prototype.onEdge = function onEdge() { | |
return this._onEdge; | |
}; | |
/** | |
* Allows you to overwrite the way Scroller lays out it's renderables. Scroller will | |
* pass an offset into the function. By default the Scroller instance just translates each node | |
* in it's direction by the passed-in offset. | |
* Scroller will translate each renderable down | |
* @method outputFrom | |
* @param {Function} fn A function that takes an offset and returns a transform. | |
* @param {Function} [masterFn] | |
*/ | |
Scroller.prototype.outputFrom = function outputFrom(fn, masterFn) { | |
if (!fn) { | |
fn = function(offset) { | |
return (this.options.direction === Utility.Direction.X) ? Transform.translate(offset, 0) : Transform.translate(0, offset); | |
}.bind(this); | |
if (!masterFn) masterFn = fn; | |
} | |
this._outputFunction = fn; | |
this._masterOutputFunction = masterFn ? masterFn : function(offset) { | |
return Transform.inverse(fn(-offset)); | |
}; | |
}; | |
/** | |
* The Scroller instance's method for reading from an external position. Scroller uses | |
* the external position to actually scroll through it's renderables. | |
* @method positionFrom | |
* @param {Getter} position Can be either a function that returns a position, | |
* or an object with a get method that returns a position. | |
*/ | |
Scroller.prototype.positionFrom = function positionFrom(position) { | |
if (position instanceof Function) this._positionGetter = position; | |
else if (position && position.get) this._positionGetter = position.get.bind(position); | |
else { | |
this._positionGetter = null; | |
this._position = position; | |
} | |
if (this._positionGetter) this._position = this._positionGetter.call(this); | |
}; | |
/** | |
* Sets the collection of renderables under the Scroller instance's control. | |
* | |
* @method sequenceFrom | |
* @param {Array|ViewSequence} items Either an array of renderables or a Famous viewSequence. | |
* @chainable | |
*/ | |
Scroller.prototype.sequenceFrom = function sequenceFrom(node) { | |
if (node instanceof Array) node = new ViewSequence({array: node}); | |
this._node = node; | |
this._positionOffset = 0; | |
}; | |
/** | |
* Returns the width and the height of the Scroller instance. | |
* | |
* @method getSize | |
* @return {Array} A two value array of the Scroller instance's current width and height (in that order). | |
*/ | |
Scroller.prototype.getSize = function getSize(actual) { | |
return actual ? this._contextSize : this._size; | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
Scroller.prototype.render = function render() { | |
if (!this._node) return null; | |
if (this._positionGetter) this._position = this._positionGetter.call(this); | |
return this._entityId; | |
}; | |
/** | |
* 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 | |
*/ | |
Scroller.prototype.commit = function commit(context) { | |
var transform = context.transform; | |
var opacity = context.opacity; | |
var origin = context.origin; | |
var size = context.size; | |
// reset edge detection on size change | |
if (!this.options.clipSize && (size[0] !== this._contextSize[0] || size[1] !== this._contextSize[1])) { | |
this._onEdge = 0; | |
this._contextSize[0] = size[0]; | |
this._contextSize[1] = size[1]; | |
if (this.options.direction === Utility.Direction.X) { | |
this._size[0] = _getClipSize.call(this); | |
this._size[1] = undefined; | |
} | |
else { | |
this._size[0] = undefined; | |
this._size[1] = _getClipSize.call(this); | |
} | |
} | |
var scrollTransform = this._masterOutputFunction(-this._position); | |
return { | |
transform: Transform.multiply(transform, scrollTransform), | |
size: size, | |
opacity: opacity, | |
origin: origin, | |
target: this.group.render() | |
}; | |
}; | |
function _normalizeState() { | |
var nodeSize = _sizeForDir.call(this, this._node.getSize()); | |
var nextNode = this._node && this._node.getNext ? this._node.getNext() : null; | |
while (nextNode && this._position + this._positionOffset >= nodeSize) { | |
this._positionOffset -= nodeSize; | |
this._node = nextNode; | |
nodeSize = _sizeForDir.call(this, this._node.getSize()); | |
nextNode = this._node && this._node.getNext ? this._node.getNext() : null; | |
} | |
var prevNode = this._node && this._node.getPrevious ? this._node.getPrevious() : null; | |
while (prevNode && this._position + this._positionOffset < 0) { | |
var prevNodeSize = _sizeForDir.call(this, prevNode.getSize()); | |
this._positionOffset += prevNodeSize; | |
this._node = prevNode; | |
prevNode = this._node && this._node.getPrevious ? this._node.getPrevious() : null; | |
} | |
} | |
function _innerRender() { | |
var size = null; | |
var position = this._position; | |
var result = []; | |
this._onEdge = 0; | |
var offset = -this._positionOffset; | |
var clipSize = _getClipSize.call(this); | |
var currNode = this._node; | |
while (currNode && offset - position < clipSize + this.options.margin) { | |
offset += _output.call(this, currNode, offset, result); | |
currNode = currNode.getNext ? currNode.getNext() : null; | |
} | |
var sizeNode = this._node; | |
var nodesSize = _sizeForDir.call(this, sizeNode.getSize()); | |
if (offset < clipSize) { | |
while (sizeNode && nodesSize < clipSize) { | |
sizeNode = sizeNode.getPrevious(); | |
if (sizeNode) nodesSize += _sizeForDir.call(this, sizeNode.getSize()); | |
} | |
sizeNode = this._node; | |
while (sizeNode && nodesSize < clipSize) { | |
sizeNode = sizeNode.getNext(); | |
if (sizeNode) nodesSize += _sizeForDir.call(this, sizeNode.getSize()); | |
} | |
} | |
var edgeSize = (nodesSize !== undefined && nodesSize < clipSize) ? nodesSize : clipSize; | |
if (!currNode && offset - position <= edgeSize) { | |
this._onEdge = 1; | |
this._eventOutput.emit('edgeHit', { | |
position: offset - edgeSize | |
}); | |
} | |
else if (!this._node.getPrevious() && position <= 0) { | |
this._onEdge = -1; | |
this._eventOutput.emit('edgeHit', { | |
position: 0 | |
}); | |
} | |
// backwards | |
currNode = (this._node && this._node.getPrevious) ? this._node.getPrevious() : null; | |
offset = -this._positionOffset; | |
if (currNode) { | |
size = currNode.getSize ? currNode.getSize() : this._contextSize; | |
offset -= _sizeForDir.call(this, size); | |
} | |
while (currNode && ((offset - position) > -(_getClipSize.call(this) + this.options.margin))) { | |
_output.call(this, currNode, offset, result); | |
currNode = currNode.getPrevious ? currNode.getPrevious() : null; | |
if (currNode) { | |
size = currNode.getSize ? currNode.getSize() : this._contextSize; | |
offset -= _sizeForDir.call(this, size); | |
} | |
} | |
_normalizeState.call(this); | |
return result; | |
} | |
module.exports = Scroller; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/Scrollview',['require','exports','module','famous/physics/PhysicsEngine','famous/physics/bodies/Particle','famous/physics/forces/Drag','famous/physics/forces/Spring','famous/core/EventHandler','famous/core/OptionsManager','famous/core/ViewSequence','famous/views/Scroller','famous/utilities/Utility','famous/inputs/GenericSync','famous/inputs/ScrollSync','famous/inputs/TouchSync'],function(require, exports, module) { | |
var PhysicsEngine = require('famous/physics/PhysicsEngine'); | |
var Particle = require('famous/physics/bodies/Particle'); | |
var Drag = require('famous/physics/forces/Drag'); | |
var Spring = require('famous/physics/forces/Spring'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var ViewSequence = require('famous/core/ViewSequence'); | |
var Scroller = require('famous/views/Scroller'); | |
var Utility = require('famous/utilities/Utility'); | |
var GenericSync = require('famous/inputs/GenericSync'); | |
var ScrollSync = require('famous/inputs/ScrollSync'); | |
var TouchSync = require('famous/inputs/TouchSync'); | |
GenericSync.register({scroll : ScrollSync, touch : TouchSync}); | |
/** | |
* Scrollview will lay out a collection of renderables sequentially in the specified direction, and will | |
* allow you to scroll through them with mousewheel or touch events. | |
* @class Scrollview | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.direction=Utility.Direction.Y] Using the direction helper found in the famous Utility | |
* module, this option will lay out the Scrollview instance's renderables either horizontally | |
* (x) or vertically (y). Utility's direction is essentially either zero (X) or one (Y), so feel free | |
* to just use integers as well. | |
* @param {Boolean} [options.rails=true] When true, Scrollview's genericSync will only process input in it's primary access. | |
* @param {Number} [clipSize=undefined] The size of the area (in pixels) that Scrollview will display content in. | |
* @param {Number} [margin=undefined] The size of the area (in pixels) that Scrollview will process renderables' associated calculations in. | |
* @param {Number} [friction=0.001] Input resistance proportional to the velocity of the input. | |
* Controls the feel of the Scrollview instance at low velocities. | |
* @param {Number} [drag=0.0001] Input resistance proportional to the square of the velocity of the input. | |
* Affects Scrollview instance more prominently at high velocities. | |
* @param {Number} [edgeGrip=0.5] A coefficient for resistance against after-touch momentum. | |
* @param {Number} [egePeriod=300] Sets the period on the spring that handles the physics associated | |
* with hitting the end of a scrollview. | |
* @param {Number} [edgeDamp=1] Sets the damping on the spring that handles the physics associated | |
* with hitting the end of a scrollview. | |
* @param {Boolean} [paginated=false] A paginated scrollview will scroll through items discretely | |
* rather than continously. | |
* @param {Number} [pagePeriod=500] Sets the period on the spring that handles the physics associated | |
* with pagination. | |
* @param {Number} [pageDamp=0.8] Sets the damping on the spring that handles the physics associated | |
* with pagination. | |
* @param {Number} [pageStopSpeed=Infinity] The threshold for determining the amount of velocity | |
* required to trigger pagination. The lower the threshold, the easier it is to scroll continuosly. | |
* @param {Number} [pageSwitchSpeed=1] The threshold for momentum-based velocity pagination. | |
* @param {Number} [speedLimit=10] The highest scrolling speed you can reach. | |
*/ | |
function Scrollview(options) { | |
this.options = Object.create(Scrollview.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
this._node = null; | |
this._physicsEngine = new PhysicsEngine(); | |
this._particle = new Particle(); | |
this._physicsEngine.addBody(this._particle); | |
this.spring = new Spring({anchor: [0, 0, 0]}); | |
this.drag = new Drag({forceFunction: Drag.FORCE_FUNCTIONS.QUADRATIC}); | |
this.friction = new Drag({forceFunction: Drag.FORCE_FUNCTIONS.LINEAR}); | |
this.sync = new GenericSync(['scroll', 'touch'], {direction : this.options.direction}); | |
this._eventInput = new EventHandler(); | |
this._eventOutput = new EventHandler(); | |
this._eventInput.pipe(this.sync); | |
this.sync.pipe(this._eventInput); | |
EventHandler.setInputHandler(this, this._eventInput); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this._touchCount = 0; | |
this._springState = 0; | |
this._onEdge = 0; // -1 for top, 1 for bottom | |
this._pageSpringPosition = 0; | |
this._edgeSpringPosition = 0; | |
this._touchVelocity = undefined; | |
this._earlyEnd = false; | |
this._needsPaginationCheck = false; | |
this._scroller = new Scroller(); | |
this._scroller.positionFrom(this.getPosition.bind(this)); | |
this.setOptions(options); | |
_bindEvents.call(this); | |
} | |
/** @const */ | |
var TOLERANCE = 0.5; | |
Scrollview.DEFAULT_OPTIONS = { | |
direction: Utility.Direction.Y, | |
rails: true, | |
friction: 0.001, | |
drag: 0.0001, | |
edgeGrip: 0.5, | |
edgePeriod: 300, | |
edgeDamp: 1, | |
margin: 1000, // mostly safe | |
paginated: false, | |
pagePeriod: 500, | |
pageDamp: 0.8, | |
pageStopSpeed: 10, | |
pageSwitchSpeed: 0.5, | |
speedLimit: 10, | |
groupScroll: false | |
}; | |
/** @enum */ | |
var SpringStates = { | |
NONE: 0, | |
EDGE: 1, | |
PAGE: 2 | |
}; | |
function _handleStart(event) { | |
this._touchCount = event.count; | |
if (event.count === undefined) this._touchCount = 1; | |
_detachAgents.call(this); | |
this.setVelocity(0); | |
this._touchVelocity = 0; | |
this._earlyEnd = false; | |
} | |
function _handleMove(event) { | |
var velocity = -event.velocity; | |
var delta = -event.delta; | |
if (this._onEdge && event.slip) { | |
if ((velocity < 0 && this._onEdge < 0) || (velocity > 0 && this._onEdge > 0)) { | |
if (!this._earlyEnd) { | |
_handleEnd.call(this, event); | |
this._earlyEnd = true; | |
} | |
} | |
else if (this._earlyEnd && (Math.abs(velocity) > Math.abs(this.getVelocity()))) { | |
_handleStart.call(this, event); | |
} | |
} | |
if (this._earlyEnd) return; | |
this._touchVelocity = velocity; | |
if (event.slip) this.setVelocity(velocity); | |
else this.setPosition(this.getPosition() + delta); | |
} | |
function _handleEnd(event) { | |
this._touchCount = event.count || 0; | |
if (!this._touchCount) { | |
_detachAgents.call(this); | |
if (this._onEdge) _setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | |
_attachAgents.call(this); | |
var velocity = -event.velocity; | |
var speedLimit = this.options.speedLimit; | |
if (event.slip) speedLimit *= this.options.edgeGrip; | |
if (velocity < -speedLimit) velocity = -speedLimit; | |
else if (velocity > speedLimit) velocity = speedLimit; | |
this.setVelocity(velocity); | |
this._touchVelocity = undefined; | |
this._needsPaginationCheck = true; | |
} | |
} | |
function _bindEvents() { | |
this._eventInput.bindThis(this); | |
this._eventInput.on('start', _handleStart); | |
this._eventInput.on('update', _handleMove); | |
this._eventInput.on('end', _handleEnd); | |
this._scroller.on('edgeHit', function(data) { | |
this._edgeSpringPosition = data.position; | |
}.bind(this)); | |
} | |
function _attachAgents() { | |
if (this._springState) this._physicsEngine.attach([this.spring], this._particle); | |
else this._physicsEngine.attach([this.drag, this.friction], this._particle); | |
} | |
function _detachAgents() { | |
this._springState = SpringStates.NONE; | |
this._physicsEngine.detachAll(); | |
} | |
function _nodeSizeForDirection(node) { | |
var direction = this.options.direction; | |
var nodeSize = (node.getSize() || this._scroller.getSize())[direction]; | |
if (!nodeSize) nodeSize = this._scroller.getSize()[direction]; | |
return nodeSize; | |
} | |
function _handleEdge(edgeDetected) { | |
if (!this._onEdge && edgeDetected) { | |
this.sync.setOptions({scale: this.options.edgeGrip}); | |
if (!this._touchCount && this._springState !== SpringStates.EDGE) { | |
_setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | |
} | |
} | |
else if (this._onEdge && !edgeDetected) { | |
this.sync.setOptions({scale: 1}); | |
if (this._springState && Math.abs(this.getVelocity()) < 0.001) { | |
// reset agents, detaching the spring | |
_detachAgents.call(this); | |
_attachAgents.call(this); | |
} | |
} | |
this._onEdge = edgeDetected; | |
} | |
function _handlePagination() { | |
if (!this._needsPaginationCheck) return; | |
if (this._touchCount) return; | |
if (this._springState === SpringStates.EDGE) return; | |
var velocity = this.getVelocity(); | |
if (Math.abs(velocity) >= this.options.pageStopSpeed) return; | |
var position = this.getPosition(); | |
var velocitySwitch = Math.abs(velocity) > this.options.pageSwitchSpeed; | |
// parameters to determine when to switch | |
var nodeSize = _nodeSizeForDirection.call(this, this._node); | |
var positionNext = position > 0.5 * nodeSize; | |
var velocityNext = velocity > 0; | |
if ((positionNext && !velocitySwitch) || (velocitySwitch && velocityNext)) this.goToNextPage(); | |
else _setSpring.call(this, 0, SpringStates.PAGE); | |
this._needsPaginationCheck = false; | |
} | |
function _setSpring(position, springState) { | |
var springOptions; | |
if (springState === SpringStates.EDGE) { | |
this._edgeSpringPosition = position; | |
springOptions = { | |
anchor: [this._edgeSpringPosition, 0, 0], | |
period: this.options.edgePeriod, | |
dampingRatio: this.options.edgeDamp | |
}; | |
} | |
else if (springState === SpringStates.PAGE) { | |
this._pageSpringPosition = position; | |
springOptions = { | |
anchor: [this._pageSpringPosition, 0, 0], | |
period: this.options.pagePeriod, | |
dampingRatio: this.options.pageDamp | |
}; | |
} | |
this.spring.setOptions(springOptions); | |
if (springState && !this._springState) { | |
_detachAgents.call(this); | |
this._springState = springState; | |
_attachAgents.call(this); | |
} | |
this._springState = springState; | |
} | |
function _normalizeState() { | |
var position = this.getPosition(); | |
var nodeSize = _nodeSizeForDirection.call(this, this._node); | |
var nextNode = this._node.getNext(); | |
while (position > nodeSize + TOLERANCE && nextNode) { | |
_shiftOrigin.call(this, -nodeSize); | |
position -= nodeSize; | |
this._scroller.sequenceFrom(nextNode); | |
this._node = nextNode; | |
nextNode = this._node.getNext(); | |
nodeSize = _nodeSizeForDirection.call(this, this._node); | |
} | |
var previousNode = this._node.getPrevious(); | |
var previousNodeSize; | |
while (position < -TOLERANCE && previousNode) { | |
previousNodeSize = _nodeSizeForDirection.call(this, previousNode); | |
this._scroller.sequenceFrom(previousNode); | |
this._node = previousNode; | |
_shiftOrigin.call(this, previousNodeSize); | |
position += previousNodeSize; | |
previousNode = this._node.getPrevious(); | |
} | |
} | |
function _shiftOrigin(amount) { | |
this._edgeSpringPosition += amount; | |
this._pageSpringPosition += amount; | |
this.setPosition(this.getPosition() + amount); | |
if (this._springState === SpringStates.EDGE) { | |
this.spring.setOptions({anchor: [this._edgeSpringPosition, 0, 0]}); | |
} | |
else if (this._springState === SpringStates.PAGE) { | |
this.spring.setOptions({anchor: [this._pageSpringPosition, 0, 0]}); | |
} | |
} | |
Scrollview.prototype.outputFrom = function outputFrom() { | |
return this._scroller.outputFrom.apply(this._scroller, arguments); | |
}; | |
/** | |
* Returns the position associated with the Scrollview instance's current node | |
* (generally the node currently at the top). | |
* @method getPosition | |
* @param {number} [node] If specified, returns the position of the node at that index in the | |
* Scrollview instance's currently managed collection. | |
* @return {number} The position of either the specified node, or the Scrollview's current Node, | |
* in pixels translated. | |
*/ | |
Scrollview.prototype.getPosition = function getPosition() { | |
return this._particle.getPosition1D(); | |
}; | |
/** | |
* Sets position of the physics particle that controls Scrollview instance's "position" | |
* @method setPosition | |
* @param {number} x The amount of pixels you want your scrollview to progress by. | |
*/ | |
Scrollview.prototype.setPosition = function setPosition(x) { | |
this._particle.setPosition1D(x); | |
}; | |
/** | |
* Returns the Scrollview instance's velocity. | |
* @method getVelocity | |
* @return {Number} The velocity. | |
*/ | |
Scrollview.prototype.getVelocity = function getVelocity() { | |
return this._touchCount ? this._touchVelocity : this._particle.getVelocity1D(); | |
}; | |
/** | |
* Sets the Scrollview instance's velocity. Until affected by input or another call of setVelocity | |
* the Scrollview instance will scroll at the passed-in velocity. | |
* @method setVelocity | |
* @param {number} v The magnitude of the velocity. | |
*/ | |
Scrollview.prototype.setVelocity = function setVelocity(v) { | |
this._particle.setVelocity1D(v); | |
}; | |
/** | |
* Patches the Scrollview instance's options with the passed-in ones. | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the Scrollview instance. | |
*/ | |
Scrollview.prototype.setOptions = function setOptions(options) { | |
if (options) { | |
if (options.direction !== undefined) { | |
if (options.direction === 'x') options.direction = Utility.Direction.X; | |
else if (options.direction === 'y') options.direction = Utility.Direction.Y; | |
} | |
this._scroller.setOptions(options); | |
this._optionsManager.setOptions(options); | |
} | |
this._scroller.setOptions(this.options); | |
if (this.options.groupScroll) | |
this._scroller.pipe(this._eventInput); | |
else | |
this._scroller.unpipe(this._eventInput); | |
this.drag.setOptions({strength: this.options.drag}); | |
this.friction.setOptions({strength: this.options.friction}); | |
this.spring.setOptions({ | |
period: this.options.edgePeriod, | |
dampingRatio: this.options.edgeDamp | |
}); | |
this.sync.setOptions({ | |
rails: this.options.rails, | |
direction: (this.options.direction === Utility.Direction.X) ? GenericSync.DIRECTION_X : GenericSync.DIRECTION_Y | |
}); | |
}; | |
/** | |
* goToPreviousPage paginates your Scrollview instance backwards by one item. | |
* @method goToPreviousPage | |
* @return {ViewSequence} The previous node. | |
*/ | |
Scrollview.prototype.goToPreviousPage = function goToPreviousPage() { | |
if (!this._node) return null; | |
var previousNode = this._node.getPrevious(); | |
if (previousNode) { | |
var currentPosition = this.getPosition(); | |
var previousNodeSize = _nodeSizeForDirection.call(this, previousNode); | |
this._scroller.sequenceFrom(previousNode); | |
this._node = previousNode; | |
var previousSpringPosition = (currentPosition < TOLERANCE) ? -previousNodeSize : 0; | |
_setSpring.call(this, previousSpringPosition, SpringStates.PAGE); | |
_shiftOrigin.call(this, previousNodeSize); | |
} | |
this._eventOutput.emit('pageChange', {direction: -1}); | |
return previousNode; | |
}; | |
/** | |
* goToNextPage paginates your Scrollview instance forwards by one item. | |
* @method goToNextPage | |
* @return {ViewSequence} The next node. | |
*/ | |
Scrollview.prototype.goToNextPage = function goToNextPage() { | |
if (!this._node) return null; | |
var nextNode = this._node.getNext(); | |
if (nextNode) { | |
var currentPosition = this.getPosition(); | |
var currentNodeSize = _nodeSizeForDirection.call(this, this._node); | |
var nextNodeSize = _nodeSizeForDirection.call(this, nextNode); | |
this._scroller.sequenceFrom(nextNode); | |
this._node = nextNode; | |
var nextSpringPosition = (currentPosition > currentNodeSize - TOLERANCE) ? currentNodeSize + nextNodeSize : currentNodeSize; | |
_setSpring.call(this, nextSpringPosition, SpringStates.PAGE); | |
_shiftOrigin.call(this, -currentNodeSize); | |
} | |
this._eventOutput.emit('pageChange', {direction: 1}); | |
return nextNode; | |
}; | |
/** | |
* Sets the collection of renderables under the Scrollview instance's control, by | |
* setting its current node to the passed in ViewSequence. If you | |
* pass in an array, the Scrollview instance will set its node as a ViewSequence instantiated with | |
* the passed-in array. | |
* | |
* @method sequenceFrom | |
* @param {Array|ViewSequence} node Either an array of renderables or a Famous viewSequence. | |
*/ | |
Scrollview.prototype.sequenceFrom = function sequenceFrom(node) { | |
if (node instanceof Array) node = new ViewSequence({array: node}); | |
this._node = node; | |
return this._scroller.sequenceFrom(node); | |
}; | |
/** | |
* Returns the width and the height of the Scrollview instance. | |
* | |
* @method getSize | |
* @return {Array} A two value array of the Scrollview instance's current width and height (in that order). | |
*/ | |
Scrollview.prototype.getSize = function getSize() { | |
return this._scroller.getSize.apply(this._scroller, arguments); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
Scrollview.prototype.render = function render() { | |
if (!this._node) return null; | |
_normalizeState.call(this); | |
_handleEdge.call(this, this._scroller.onEdge()); | |
if (this.options.paginated) _handlePagination.call(this); | |
return this._scroller.render(); | |
}; | |
module.exports = Scrollview; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/views/ScrollContainer',['require','exports','module','famous/surfaces/ContainerSurface','famous/core/EventHandler','./Scrollview','famous/utilities/Utility','famous/core/OptionsManager'],function(require, exports, module) { | |
var ContainerSurface = require('famous/surfaces/ContainerSurface'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Scrollview = require('./Scrollview'); | |
var Utility = require('famous/utilities/Utility'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
/** | |
* A Container surface with a scrollview automatically added. The convenience of ScrollContainer lies in | |
* being able to clip out portions of the associated scrollview that lie outside the bounding surface, | |
* and in being able to move the scrollview more easily by applying modifiers to the parent container | |
* surface. | |
* @class ScrollContainer | |
* @constructor | |
* @param {Options} [options] An object of configurable options. | |
* @param {Options} [options.container=undefined] Options for the ScrollContainer instance's surface. | |
* @param {Options} [options.scrollview={direction:Utility.Direction.X}] Options for the ScrollContainer instance's scrollview. | |
*/ | |
function ScrollContainer(options) { | |
this.options = Object.create(ScrollContainer.DEFAULT_OPTIONS); | |
this._optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this.container = new ContainerSurface(this.options.container); | |
this.scrollview = new Scrollview(this.options.scrollview); | |
this.container.add(this.scrollview); | |
EventHandler.setInputHandler(this, this.scrollview); | |
EventHandler.setOutputHandler(this, this.scrollview); | |
this.scrollview.subscribe(this.container); | |
} | |
ScrollContainer.DEFAULT_OPTIONS = { | |
container: { | |
properties: {overflow : 'hidden'} | |
}, | |
scrollview: {direction: Utility.Direction.X} | |
}; | |
/** | |
* Patches the ScrollContainer instance's options with the passed-in ones. | |
* | |
* @method setOptions | |
* @param {Options} options An object of configurable options for the ScrollContainer instance. | |
*/ | |
ScrollContainer.prototype.setOptions = function setOptions(options) { | |
return this._optionsManager.setOptions(options); | |
}; | |
/** | |
* Sets the collection of renderables under the ScrollContainer instance scrollview's control. | |
* | |
* @method sequenceFrom | |
* @param {Array|ViewSequence} sequence Either an array of renderables or a Famous ViewSequence. | |
*/ | |
ScrollContainer.prototype.sequenceFrom = function sequenceFrom() { | |
return this.scrollview.sequenceFrom.apply(this.scrollview, arguments); | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
ScrollContainer.prototype.render = function render() { | |
return this.container.render.apply(this.container, arguments); | |
}; | |
module.exports = ScrollContainer; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/widgets/NavigationBar',['require','exports','module','famous/core/Scene','famous/core/Surface','famous/core/Transform','famous/core/View'],function(require, exports, module) { | |
var Scene = require('famous/core/Scene'); | |
var Surface = require('famous/core/Surface'); | |
var Transform = require('famous/core/Transform'); | |
var View = require('famous/core/View'); | |
/** | |
* A view for displaying the title of the current page | |
* as well as icons for navigating backwards and opening | |
* further options | |
* | |
* @class NavigationBar | |
* @extends View | |
* @constructor | |
* | |
* @param {object} [options] overrides of default options | |
* @param {Array.number} [options.size=(undefined,0.5)] Size of the navigation bar and it's componenets. | |
* @param {Array.string} [options.backClasses=(back)] CSS Classes attached to back of Navigation. | |
* @param {String} [options.backContent=(◀)] Content of the back button. | |
* @param {Array.string} [options.classes=(navigation)] CSS Classes attached to the surfaces. | |
* @param {String} [options.content] Content to pass into title bar. | |
* @param {Array.string} [options.classes=(more)] CSS Classes attached to the More surface. | |
* @param {String} [options.moreContent=(✚)] Content of the more button. | |
*/ | |
function NavigationBar(options) { | |
View.apply(this, arguments); | |
this.title = new Surface({ | |
classes: this.options.classes, | |
content: this.options.content | |
}); | |
this.back = new Surface({ | |
size: [this.options.size[1], this.options.size[1]], | |
classes: this.options.classes, | |
content: this.options.backContent | |
}); | |
this.back.on('click', function() { | |
this._eventOutput.emit('back', {}); | |
}.bind(this)); | |
this.more = new Surface({ | |
size: [this.options.size[1], this.options.size[1]], | |
classes: this.options.classes, | |
content: this.options.moreContent | |
}); | |
this.more.on('click', function() { | |
this._eventOutput.emit('more', {}); | |
}.bind(this)); | |
this.layout = new Scene({ | |
id: 'master', | |
size: this.options.size, | |
target: [ | |
{ | |
transform: Transform.inFront, | |
origin: [0, 0.5], | |
target: this.back | |
}, | |
{ | |
origin: [0.5, 0.5], | |
target: this.title | |
}, | |
{ | |
transform: Transform.inFront, | |
origin: [1, 0.5], | |
target: this.more | |
} | |
] | |
}); | |
this._add(this.layout); | |
this._optionsManager.on('change', function(event) { | |
var key = event.id; | |
var data = event.value; | |
if (key === 'size') { | |
this.layout.id.master.setSize(data); | |
this.title.setSize(data); | |
this.back.setSize([data[1], data[1]]); | |
this.more.setSize([data[1], data[1]]); | |
} | |
else if (key === 'backClasses') { | |
this.back.setOptions({classes: this.options.classes.concat(this.options.backClasses)}); | |
} | |
else if (key === 'backContent') { | |
this.back.setContent(this.options.backContent); | |
} | |
else if (key === 'classes') { | |
this.title.setOptions({classes: this.options.classes}); | |
this.back.setOptions({classes: this.options.classes.concat(this.options.backClasses)}); | |
this.more.setOptions({classes: this.options.classes.concat(this.options.moreClasses)}); | |
} | |
else if (key === 'content') { | |
this.setContent(this.options.content); | |
} | |
else if (key === 'moreClasses') { | |
this.more.setOptions({classes: this.options.classes.concat(this.options.moreClasses)}); | |
} | |
else if (key === 'moreContent') { | |
this.more.setContent(this.options.content); | |
} | |
}.bind(this)); | |
} | |
NavigationBar.prototype = Object.create(View.prototype); | |
NavigationBar.prototype.constructor = NavigationBar; | |
NavigationBar.DEFAULT_OPTIONS = { | |
size: [undefined, 50], | |
backClasses: ['back'], | |
backContent: '◀', | |
classes: ['navigation'], | |
content: '', | |
moreClasses: ['more'], | |
moreContent: '✚' | |
}; | |
/** | |
* Set the title of the NavigationBar | |
* | |
* @method setContent | |
* | |
* @param {object} content JSON object containing title information | |
* | |
* @return {undefined} | |
*/ | |
NavigationBar.prototype.setContent = function setContent(content) { | |
return this.title.setContent(content); | |
}; | |
module.exports = NavigationBar; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/widgets/Slider',['require','exports','module','famous/core/Surface','famous/surfaces/CanvasSurface','famous/core/Transform','famous/core/EventHandler','famous/math/Utilities','famous/core/OptionsManager','famous/inputs/MouseSync','famous/inputs/TouchSync','famous/inputs/GenericSync'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
var CanvasSurface = require('famous/surfaces/CanvasSurface'); | |
var Transform = require('famous/core/Transform'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var Utilities = require('famous/math/Utilities'); | |
var OptionsManager = require('famous/core/OptionsManager'); | |
var MouseSync = require('famous/inputs/MouseSync'); | |
var TouchSync = require('famous/inputs/TouchSync'); | |
var GenericSync = require('famous/inputs/GenericSync'); | |
GenericSync.register({ | |
mouse : MouseSync, | |
touch : TouchSync | |
}); | |
/** @constructor */ | |
function Slider(options) { | |
this.options = Object.create(Slider.DEFAULT_OPTIONS); | |
this.optionsManager = new OptionsManager(this.options); | |
if (options) this.setOptions(options); | |
this.indicator = new CanvasSurface({ | |
size: this.options.indicatorSize, | |
classes : ['slider-back'] | |
}); | |
this.label = new Surface({ | |
size: this.options.labelSize, | |
content: this.options.label, | |
properties : {pointerEvents : 'none'}, | |
classes: ['slider-label'] | |
}); | |
this.eventOutput = new EventHandler(); | |
this.eventInput = new EventHandler(); | |
EventHandler.setInputHandler(this, this.eventInput); | |
EventHandler.setOutputHandler(this, this.eventOutput); | |
var scale = (this.options.range[1] - this.options.range[0]) / this.options.indicatorSize[0]; | |
this.sync = new GenericSync( | |
['mouse', 'touch'], | |
{ | |
scale : scale, | |
direction : GenericSync.DIRECTION_X | |
} | |
); | |
this.indicator.pipe(this.sync); | |
this.sync.pipe(this); | |
this.eventInput.on('update', function(data) { | |
this.set(data.position); | |
}.bind(this)); | |
this._drawPos = 0; | |
_updateLabel.call(this); | |
} | |
Slider.DEFAULT_OPTIONS = { | |
size: [200, 60], | |
indicatorSize: [200, 30], | |
labelSize: [200, 30], | |
range: [0, 1], | |
precision: 2, | |
value: 0, | |
label: '', | |
fillColor: 'rgba(170, 170, 170, 1)' | |
}; | |
function _updateLabel() { | |
this.label.setContent(this.options.label + '<span style="float: right">' + this.get().toFixed(this.options.precision) + '</span>'); | |
} | |
Slider.prototype.setOptions = function setOptions(options) { | |
return this.optionsManager.setOptions(options); | |
}; | |
Slider.prototype.get = function get() { | |
return this.options.value; | |
}; | |
Slider.prototype.set = function set(value) { | |
if (value === this.options.value) return; | |
this.options.value = Utilities.clamp(value, this.options.range); | |
_updateLabel.call(this); | |
this.eventOutput.emit('change', {value: value}); | |
}; | |
Slider.prototype.getSize = function getSize() { | |
return this.options.size; | |
}; | |
Slider.prototype.render = function render() { | |
var range = this.options.range; | |
var fillSize = Math.floor(((this.get() - range[0]) / (range[1] - range[0])) * this.options.indicatorSize[0]); | |
if (fillSize < this._drawPos) { | |
this.indicator.getContext('2d').clearRect(fillSize, 0, this._drawPos - fillSize + 1, this.options.indicatorSize[1]); | |
} | |
else if (fillSize > this._drawPos) { | |
var ctx = this.indicator.getContext('2d'); | |
ctx.fillStyle = this.options.fillColor; | |
ctx.fillRect(this._drawPos-1, 0, fillSize - this._drawPos+1, this.options.indicatorSize[1]); | |
} | |
this._drawPos = fillSize; | |
return { | |
size: this.options.size, | |
target: [ | |
{ | |
origin: [0, 0], | |
target: this.indicator.render() | |
}, | |
{ | |
transform: Transform.translate(0, 0, 1), | |
origin: [0, 0], | |
target: this.label.render() | |
} | |
] | |
}; | |
}; | |
module.exports = Slider; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/widgets/ToggleButton',['require','exports','module','famous/core/Surface','famous/core/EventHandler','famous/views/RenderController'],function(require, exports, module) { | |
var Surface = require('famous/core/Surface'); | |
var EventHandler = require('famous/core/EventHandler'); | |
var RenderController = require('famous/views/RenderController'); | |
/** | |
* A view for transitioning between two surfaces based | |
* on a 'on' and 'off' state | |
* | |
* @class TabBar | |
* @extends View | |
* @constructor | |
* | |
* @param {object} options overrides of default options | |
*/ | |
function ToggleButton(options) { | |
this.options = { | |
content: '', | |
offClasses: ['off'], | |
onClasses: ['on'], | |
size: undefined, | |
outTransition: {curve: 'easeInOut', duration: 300}, | |
inTransition: {curve: 'easeInOut', duration: 300}, | |
toggleMode: ToggleButton.TOGGLE, | |
crossfade: true | |
}; | |
this._eventOutput = new EventHandler(); | |
EventHandler.setOutputHandler(this, this._eventOutput); | |
this.offSurface = new Surface(); | |
this.offSurface.on('click', function() { | |
if (this.options.toggleMode !== ToggleButton.OFF) this.select(); | |
}.bind(this)); | |
this.offSurface.pipe(this._eventOutput); | |
this.onSurface = new Surface(); | |
this.onSurface.on('click', function() { | |
if (this.options.toggleMode !== ToggleButton.ON) this.deselect(); | |
}.bind(this)); | |
this.onSurface.pipe(this._eventOutput); | |
this.arbiter = new RenderController({ | |
overlap : this.options.crossfade | |
}); | |
this.deselect(); | |
if (options) this.setOptions(options); | |
} | |
ToggleButton.OFF = 0; | |
ToggleButton.ON = 1; | |
ToggleButton.TOGGLE = 2; | |
/** | |
* Transition towards the 'on' state and dispatch an event to | |
* listeners to announce it was selected | |
* | |
* @method select | |
*/ | |
ToggleButton.prototype.select = function select() { | |
this.selected = true; | |
this.arbiter.show(this.onSurface, this.options.inTransition); | |
// this.arbiter.setMode(ToggleButton.ON, this.options.inTransition); | |
this._eventOutput.emit('select'); | |
}; | |
/** | |
* Transition towards the 'off' state and dispatch an event to | |
* listeners to announce it was deselected | |
* | |
* @method deselect | |
*/ | |
ToggleButton.prototype.deselect = function deselect() { | |
this.selected = false; | |
this.arbiter.show(this.offSurface, this.options.outTransition); | |
this._eventOutput.emit('deselect'); | |
}; | |
/** | |
* Return the state of the button | |
* | |
* @method isSelected | |
* | |
* @return {boolean} selected state | |
*/ | |
ToggleButton.prototype.isSelected = function isSelected() { | |
return this.selected; | |
}; | |
/** | |
* Override the current options | |
* | |
* @method setOptions | |
* | |
* @param {object} options JSON | |
*/ | |
ToggleButton.prototype.setOptions = function setOptions(options) { | |
if (options.content !== undefined) { | |
this.options.content = options.content; | |
this.offSurface.setContent(this.options.content); | |
this.onSurface.setContent(this.options.content); | |
} | |
if (options.offClasses) { | |
this.options.offClasses = options.offClasses; | |
this.offSurface.setClasses(this.options.offClasses); | |
} | |
if (options.onClasses) { | |
this.options.onClasses = options.onClasses; | |
this.onSurface.setClasses(this.options.onClasses); | |
} | |
if (options.size !== undefined) { | |
this.options.size = options.size; | |
this.onSurface.setSize(this.options.size); | |
this.offSurface.setSize(this.options.size); | |
} | |
if (options.toggleMode !== undefined) this.options.toggleMode = options.toggleMode; | |
if (options.outTransition !== undefined) this.options.outTransition = options.outTransition; | |
if (options.inTransition !== undefined) this.options.inTransition = options.inTransition; | |
if (options.crossfade !== undefined) { | |
this.options.crossfade = options.crossfade; | |
this.arbiter.setOptions({overlap: this.options.crossfade}); | |
} | |
}; | |
/** | |
* Return the size defined in the options object | |
* | |
* @method getSize | |
* | |
* @return {array} two element array [height, width] | |
*/ | |
ToggleButton.prototype.getSize = function getSize() { | |
return this.options.size; | |
}; | |
/** | |
* Generate a render spec from the contents of this component. | |
* | |
* @private | |
* @method render | |
* @return {number} Render spec for this component | |
*/ | |
ToggleButton.prototype.render = function render() { | |
return this.arbiter.render(); | |
}; | |
module.exports = ToggleButton; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/widgets/TabBar',['require','exports','module','famous/utilities/Utility','famous/core/View','famous/views/GridLayout','./ToggleButton'],function(require, exports, module) { | |
var Utility = require('famous/utilities/Utility'); | |
var View = require('famous/core/View'); | |
var GridLayout = require('famous/views/GridLayout'); | |
var ToggleButton = require('./ToggleButton'); | |
/** | |
* A view for displaying various tabs that dispatch events | |
* based on the id of the button that was clicked | |
* | |
* @class TabBar | |
* @extends View | |
* @constructor | |
* | |
* @param {object} options overrides of default options | |
*/ | |
function TabBar(options) { | |
View.apply(this, arguments); | |
this.layout = new GridLayout(); | |
this.buttons = []; | |
this._buttonIds = {}; | |
this._buttonCallbacks = {}; | |
this.layout.sequenceFrom(this.buttons); | |
this._add(this.layout); | |
this._optionsManager.on('change', _updateOptions.bind(this)); | |
} | |
TabBar.prototype = Object.create(View.prototype); | |
TabBar.prototype.constructor = TabBar; | |
TabBar.DEFAULT_OPTIONS = { | |
sections: [], | |
widget: ToggleButton, | |
size: [undefined, 50], | |
direction: Utility.Direction.X, | |
buttons: { | |
toggleMode: ToggleButton.ON | |
} | |
}; | |
/** | |
* Update the options for all components of the view | |
* | |
* @method _updateOptions | |
* | |
* @param {object} data component options | |
*/ | |
function _updateOptions(data) { | |
var id = data.id; | |
var value = data.value; | |
if (id === 'direction') { | |
this.layout.setOptions({dimensions: _resolveGridDimensions.call(this.buttons.length, this.options.direction)}); | |
} | |
else if (id === 'buttons') { | |
for (var i in this.buttons) { | |
this.buttons[i].setOptions(value); | |
} | |
} | |
else if (id === 'sections') { | |
for (var sectionId in this.options.sections) { | |
this.defineSection(sectionId, this.options.sections[sectionId]); | |
} | |
} | |
} | |
/** | |
* Return an array of the proper dimensions for the tabs | |
* | |
* @method _resolveGridDimensions | |
* | |
* @param {number} count number of buttons | |
* @param {number} direction direction of the layout | |
* | |
* @return {array} the dimensions of the tab section | |
*/ | |
function _resolveGridDimensions(count, direction) { | |
if (direction === Utility.Direction.X) return [count, 1]; | |
else return [1, count]; | |
} | |
/** | |
* Create a new button with the specified id. If one already exists with | |
* that id, unbind all listeners. | |
* | |
* @method defineSection | |
* | |
* @param {string} id name of the button | |
* @param {object} content data for the creation of a new ToggleButton | |
*/ | |
TabBar.prototype.defineSection = function defineSection(id, content) { | |
var button; | |
var i = this._buttonIds[id]; | |
if (i === undefined) { | |
i = this.buttons.length; | |
this._buttonIds[id] = i; | |
var widget = this.options.widget; | |
button = new widget(); | |
this.buttons[i] = button; | |
this.layout.setOptions({dimensions: _resolveGridDimensions(this.buttons.length, this.options.direction)}); | |
} | |
else { | |
button = this.buttons[i]; | |
button.unbind('select', this._buttonCallbacks[id]); | |
} | |
if (this.options.buttons) button.setOptions(this.options.buttons); | |
button.setOptions(content); | |
this._buttonCallbacks[id] = this.select.bind(this, id); | |
button.on('select', this._buttonCallbacks[id]); | |
}; | |
/** | |
* Select a particular button and dispatch the id of the selection | |
* to any listeners. Deselect all others | |
* | |
* @method select | |
* | |
* @param {string} id button id | |
*/ | |
TabBar.prototype.select = function select(id) { | |
var btn = this._buttonIds[id]; | |
// this prevents event loop | |
if (this.buttons[btn] && this.buttons[btn].isSelected()) { | |
this._eventOutput.emit('select', {id: id}); | |
} | |
else if (this.buttons[btn]) { | |
this.buttons[btn].select(); | |
} | |
for (var i = 0; i < this.buttons.length; i++) { | |
if (i !== btn) this.buttons[i].deselect(); | |
} | |
}; | |
module.exports = TabBar; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/bodies/Body',['require','exports','module','./Particle','famous/core/Transform','famous/math/Vector','famous/math/Quaternion','famous/math/Matrix'],function(require, exports, module) { | |
var Particle = require('./Particle'); | |
var Transform = require('famous/core/Transform'); | |
var Vector = require('famous/math/Vector'); | |
var Quaternion = require('famous/math/Quaternion'); | |
var Matrix = require('famous/math/Matrix'); | |
/** | |
* A unit controlled by the physics engine which extends the zero-dimensional | |
* Particle to include geometry. In addition to maintaining the state | |
* of a Particle its state includes orientation, angular velocity | |
* and angular momentum and responds to torque forces. | |
* | |
* @class Body | |
* @extends Particle | |
* @constructor | |
*/ | |
function Body(options) { | |
Particle.call(this, options); | |
options = options || {}; | |
this.orientation = new Quaternion(); | |
this.angularVelocity = new Vector(); | |
this.angularMomentum = new Vector(); | |
this.torque = new Vector(); | |
if (options.orientation) this.orientation.set(options.orientation); | |
if (options.angularVelocity) this.angularVelocity.set(options.angularVelocity); | |
if (options.angularMomentum) this.angularMomentum.set(options.angularMomentum); | |
if (options.torque) this.torque.set(options.torque); | |
this.setMomentsOfInertia(); | |
this.angularVelocity.w = 0; //quaternify the angular velocity | |
//registers | |
this.pWorld = new Vector(); //placeholder for world space position | |
} | |
Body.DEFAULT_OPTIONS = Particle.DEFAULT_OPTIONS; | |
Body.DEFAULT_OPTIONS.orientation = [0,0,0,1]; | |
Body.DEFAULT_OPTIONS.angularVelocity = [0,0,0]; | |
Body.AXES = Particle.AXES; | |
Body.SLEEP_TOLERANCE = Particle.SLEEP_TOLERANCE; | |
Body.INTEGRATOR = Particle.INTEGRATOR; | |
Body.prototype = Object.create(Particle.prototype); | |
Body.prototype.constructor = Body; | |
Body.prototype.isBody = true; | |
Body.prototype.setMass = function setMass() { | |
Particle.prototype.setMass.apply(this, arguments); | |
this.setMomentsOfInertia(); | |
}; | |
/** | |
* Setter for moment of inertia, which is necessary to give proper | |
* angular inertia depending on the geometry of the body. | |
* | |
* @method setMomentsOfInertia | |
*/ | |
Body.prototype.setMomentsOfInertia = function setMomentsOfInertia() { | |
this.inertia = new Matrix(); | |
this.inverseInertia = new Matrix(); | |
}; | |
/** | |
* Update the angular velocity from the angular momentum state. | |
* | |
* @method updateAngularVelocity | |
*/ | |
Body.prototype.updateAngularVelocity = function updateAngularVelocity() { | |
this.angularVelocity.set(this.inverseInertia.vectorMultiply(this.angularMomentum)); | |
}; | |
/** | |
* Determine world coordinates from the local coordinate system. Useful | |
* if the Body has rotated in space. | |
* | |
* @method toWorldCoordinates | |
* @param localPosition {Vector} local coordinate vector | |
* @return global coordinate vector {Vector} | |
*/ | |
Body.prototype.toWorldCoordinates = function toWorldCoordinates(localPosition) { | |
return this.pWorld.set(this.orientation.rotateVector(localPosition)); | |
}; | |
/** | |
* Calculates the kinetic and intertial energy of a body. | |
* | |
* @method getEnergy | |
* @return energy {Number} | |
*/ | |
Body.prototype.getEnergy = function getEnergy() { | |
return Particle.prototype.getEnergy.call(this) | |
+ 0.5 * this.inertia.vectorMultiply(this.angularVelocity).dot(this.angularVelocity); | |
}; | |
/** | |
* Extends Particle.reset to reset orientation, angular velocity | |
* and angular momentum. | |
* | |
* @method reset | |
* @param [p] {Array|Vector} position | |
* @param [v] {Array|Vector} velocity | |
* @param [q] {Array|Quaternion} orientation | |
* @param [L] {Array|Vector} angular momentum | |
*/ | |
Body.prototype.reset = function reset(p, v, q, L) { | |
Particle.prototype.reset.call(this, p, v); | |
this.angularVelocity.clear(); | |
this.setOrientation(q || [1,0,0,0]); | |
this.setAngularMomentum(L || [0,0,0]); | |
}; | |
/** | |
* Setter for orientation | |
* | |
* @method setOrientation | |
* @param q {Array|Quaternion} orientation | |
*/ | |
Body.prototype.setOrientation = function setOrientation(q) { | |
this.orientation.set(q); | |
}; | |
/** | |
* Setter for angular velocity | |
* | |
* @method setAngularVelocity | |
* @param w {Array|Vector} angular velocity | |
*/ | |
Body.prototype.setAngularVelocity = function setAngularVelocity(w) { | |
this.wake(); | |
this.angularVelocity.set(w); | |
}; | |
/** | |
* Setter for angular momentum | |
* | |
* @method setAngularMomentum | |
* @param L {Array|Vector} angular momentum | |
*/ | |
Body.prototype.setAngularMomentum = function setAngularMomentum(L) { | |
this.wake(); | |
this.angularMomentum.set(L); | |
}; | |
/** | |
* Extends Particle.applyForce with an optional argument | |
* to apply the force at an off-centered location, resulting in a torque. | |
* | |
* @method applyForce | |
* @param force {Vector} force | |
* @param [location] {Vector} off-center location on the body | |
*/ | |
Body.prototype.applyForce = function applyForce(force, location) { | |
Particle.prototype.applyForce.call(this, force); | |
if (location !== undefined) this.applyTorque(location.cross(force)); | |
}; | |
/** | |
* Applied a torque force to a body, inducing a rotation. | |
* | |
* @method applyTorque | |
* @param torque {Vector} torque | |
*/ | |
Body.prototype.applyTorque = function applyTorque(torque) { | |
this.wake(); | |
this.torque.set(this.torque.add(torque)); | |
}; | |
/** | |
* Extends Particle.getTransform to include a rotational component | |
* derived from the particle's orientation. | |
* | |
* @method getTransform | |
* @return transform {Transform} | |
*/ | |
Body.prototype.getTransform = function getTransform() { | |
return Transform.thenMove( | |
this.orientation.getTransform(), | |
Transform.getTranslate(Particle.prototype.getTransform.call(this)) | |
); | |
}; | |
/** | |
* Extends Particle._integrate to also update the rotational states | |
* of the body. | |
* | |
* @method getTransform | |
* @protected | |
* @param dt {Number} delta time | |
*/ | |
Body.prototype._integrate = function _integrate(dt) { | |
Particle.prototype._integrate.call(this, dt); | |
this.integrateAngularMomentum(dt); | |
this.updateAngularVelocity(dt); | |
this.integrateOrientation(dt); | |
}; | |
/** | |
* Updates the angular momentum via the its integrator. | |
* | |
* @method integrateAngularMomentum | |
* @param dt {Number} delta time | |
*/ | |
Body.prototype.integrateAngularMomentum = function integrateAngularMomentum(dt) { | |
Body.INTEGRATOR.integrateAngularMomentum(this, dt); | |
}; | |
/** | |
* Updates the orientation via the its integrator. | |
* | |
* @method integrateOrientation | |
* @param dt {Number} delta time | |
*/ | |
Body.prototype.integrateOrientation = function integrateOrientation(dt) { | |
Body.INTEGRATOR.integrateOrientation(this, dt); | |
}; | |
module.exports = Body; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/bodies/Circle',['require','exports','module','./Body','famous/math/Matrix'],function(require, exports, module) { | |
var Body = require('./Body'); | |
var Matrix = require('famous/math/Matrix'); | |
/** | |
* Implements a circle, or spherical, geometry for an Body with | |
* radius. | |
* | |
* @class Circle | |
* @extends Body | |
* @constructor | |
*/ | |
function Circle(options) { | |
options = options || {}; | |
this.setRadius(options.radius || 0); | |
Body.call(this, options); | |
} | |
Circle.prototype = Object.create(Body.prototype); | |
Circle.prototype.constructor = Circle; | |
/** | |
* Basic setter for radius. | |
* @method setRadius | |
* @param r {Number} radius | |
*/ | |
Circle.prototype.setRadius = function setRadius(r) { | |
this.radius = r; | |
this.size = [2*this.radius, 2*this.radius]; | |
this.setMomentsOfInertia(); | |
}; | |
Circle.prototype.setMomentsOfInertia = function setMomentsOfInertia() { | |
var m = this.mass; | |
var r = this.radius; | |
this.inertia = new Matrix([ | |
[0.25 * m * r * r, 0, 0], | |
[0, 0.25 * m * r * r, 0], | |
[0, 0, 0.5 * m * r * r] | |
]); | |
this.inverseInertia = new Matrix([ | |
[4 / (m * r * r), 0, 0], | |
[0, 4 / (m * r * r), 0], | |
[0, 0, 2 / (m * r * r)] | |
]); | |
}; | |
module.exports = Circle; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/bodies/Rectangle',['require','exports','module','./Body','famous/math/Matrix'],function(require, exports, module) { | |
var Body = require('./Body'); | |
var Matrix = require('famous/math/Matrix'); | |
/** | |
* Implements a rectangular geometry for an Body with | |
* size = [width, height]. | |
* | |
* @class Rectangle | |
* @extends Body | |
* @constructor | |
*/ | |
function Rectangle(options) { | |
options = options || {}; | |
this.size = options.size || [0,0]; | |
Body.call(this, options); | |
} | |
Rectangle.prototype = Object.create(Body.prototype); | |
Rectangle.prototype.constructor = Rectangle; | |
/** | |
* Basic setter for size. | |
* @method setSize | |
* @param size {Array} size = [width, height] | |
*/ | |
Rectangle.prototype.setSize = function setSize(size) { | |
this.size = size; | |
this.setMomentsOfInertia(); | |
}; | |
Rectangle.prototype.setMomentsOfInertia = function setMomentsOfInertia() { | |
var m = this.mass; | |
var w = this.size[0]; | |
var h = this.size[1]; | |
this.inertia = new Matrix([ | |
[m * h * h / 12, 0, 0], | |
[0, m * w * w / 12, 0], | |
[0, 0, m * (w * w + h * h) / 12] | |
]); | |
this.inverseInertia = new Matrix([ | |
[12 / (m * h * h), 0, 0], | |
[0, 12 / (m * w * w), 0], | |
[0, 0, 12 / (m * (w * w + h * h))] | |
]); | |
}; | |
module.exports = Rectangle; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Collision',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* Allows for two circular bodies to collide and bounce off each other. | |
* | |
* @class Collision | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Number} [options.restitution] The energy ratio lost in a collision (0 = stick, 1 = elastic) Range : [0, 1] | |
* @param {Number} [options.drift] Baumgarte stabilization parameter. Makes constraints "loosely" (0) or "tightly" (1) enforced. Range : [0, 1] | |
* @param {Number} [options.slop] Amount of penetration in pixels to ignore before collision event triggers | |
* | |
*/ | |
function Collision(options) { | |
this.options = Object.create(Collision.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.normal = new Vector(); | |
this.pDiff = new Vector(); | |
this.vDiff = new Vector(); | |
this.impulse1 = new Vector(); | |
this.impulse2 = new Vector(); | |
Constraint.call(this); | |
} | |
Collision.prototype = Object.create(Constraint.prototype); | |
Collision.prototype.constructor = Collision; | |
Collision.DEFAULT_OPTIONS = { | |
restitution : 0.5, | |
drift : 0.5, | |
slop : 0 | |
}; | |
function _normalVelocity(particle1, particle2) { | |
return particle1.velocity.dot(particle2.velocity); | |
} | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Collision.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds an impulse to a physics body's velocity due to the constraint | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply the constraint to | |
* @param source {Body} The source of the constraint | |
* @param dt {Number} Delta time | |
*/ | |
Collision.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
if (source === undefined) return; | |
var v1 = source.velocity; | |
var p1 = source.position; | |
var w1 = source.inverseMass; | |
var r1 = source.radius; | |
var options = this.options; | |
var drift = options.drift; | |
var slop = -options.slop; | |
var restitution = options.restitution; | |
var n = this.normal; | |
var pDiff = this.pDiff; | |
var vDiff = this.vDiff; | |
var impulse1 = this.impulse1; | |
var impulse2 = this.impulse2; | |
for (var i = 0; i < targets.length; i++) { | |
var target = targets[i]; | |
if (target === source) continue; | |
var v2 = target.velocity; | |
var p2 = target.position; | |
var w2 = target.inverseMass; | |
var r2 = target.radius; | |
pDiff.set(p2.sub(p1)); | |
vDiff.set(v2.sub(v1)); | |
var dist = pDiff.norm(); | |
var overlap = dist - (r1 + r2); | |
var effMass = 1/(w1 + w2); | |
var gamma = 0; | |
if (overlap < 0) { | |
n.set(pDiff.normalize()); | |
if (this._eventOutput) { | |
var collisionData = { | |
target : target, | |
source : source, | |
overlap : overlap, | |
normal : n | |
}; | |
this._eventOutput.emit('preCollision', collisionData); | |
this._eventOutput.emit('collision', collisionData); | |
} | |
var lambda = (overlap <= slop) | |
? ((1 + restitution) * n.dot(vDiff) + drift/dt * (overlap - slop)) / (gamma + dt/effMass) | |
: ((1 + restitution) * n.dot(vDiff)) / (gamma + dt/effMass); | |
n.mult(dt*lambda).put(impulse1); | |
impulse1.mult(-1).put(impulse2); | |
source.applyImpulse(impulse1); | |
target.applyImpulse(impulse2); | |
//source.setPosition(p1.add(n.mult(overlap/2))); | |
//target.setPosition(p2.sub(n.mult(overlap/2))); | |
if (this._eventOutput) this._eventOutput.emit('postCollision', collisionData); | |
} | |
} | |
}; | |
module.exports = Collision; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Curve',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A constraint that keeps a physics body on a given implicit curve | |
* regardless of other physical forces are applied to it. | |
* | |
* A curve constraint is two surface constraints in disguise, as a curve is | |
* the intersection of two surfaces, and is essentially constrained to both | |
* | |
* @class Curve | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Function} [options.equation] An implicitly defined surface f(x,y,z) = 0 that body is constrained to e.g. function(x,y,z) { x*x + y*y - r*r } corresponds to a circle of radius r pixels | |
* @param {Function} [options.plane] An implicitly defined second surface that the body is constrained to | |
* @param {Number} [options.period] The spring-like reaction when the constraint is violated | |
* @param {Number} [options.number] The damping-like reaction when the constraint is violated | |
*/ | |
function Curve(options) { | |
this.options = Object.create(Curve.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.J = new Vector(); | |
this.impulse = new Vector(); | |
Constraint.call(this); | |
} | |
Curve.prototype = Object.create(Constraint.prototype); | |
Curve.prototype.constructor = Curve; | |
/** @const */ var epsilon = 1e-7; | |
/** @const */ var pi = Math.PI; | |
Curve.DEFAULT_OPTIONS = { | |
equation : function(x,y,z) { | |
return 0; | |
}, | |
plane : function(x,y,z) { | |
return z; | |
}, | |
period : 0, | |
dampingRatio : 0 | |
}; | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Curve.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds a curve impulse to a physics body. | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply force to. | |
* @param source {Body} Not applicable | |
* @param dt {Number} Delta time | |
*/ | |
Curve.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
var options = this.options; | |
var impulse = this.impulse; | |
var J = this.J; | |
var f = options.equation; | |
var g = options.plane; | |
var dampingRatio = options.dampingRatio; | |
var period = options.period; | |
for (var i = 0; i < targets.length; i++) { | |
var body = targets[i]; | |
var v = body.velocity; | |
var p = body.position; | |
var m = body.mass; | |
var gamma; | |
var beta; | |
if (period === 0) { | |
gamma = 0; | |
beta = 1; | |
} | |
else { | |
var c = 4 * m * pi * dampingRatio / period; | |
var k = 4 * m * pi * pi / (period * period); | |
gamma = 1 / (c + dt*k); | |
beta = dt*k / (c + dt*k); | |
} | |
var x = p.x; | |
var y = p.y; | |
var z = p.z; | |
var f0 = f(x, y, z); | |
var dfx = (f(x + epsilon, p, p) - f0) / epsilon; | |
var dfy = (f(x, y + epsilon, p) - f0) / epsilon; | |
var dfz = (f(x, y, p + epsilon) - f0) / epsilon; | |
var g0 = g(x, y, z); | |
var dgx = (g(x + epsilon, y, z) - g0) / epsilon; | |
var dgy = (g(x, y + epsilon, z) - g0) / epsilon; | |
var dgz = (g(x, y, z + epsilon) - g0) / epsilon; | |
J.setXYZ(dfx + dgx, dfy + dgy, dfz + dgz); | |
var antiDrift = beta/dt * (f0 + g0); | |
var lambda = -(J.dot(v) + antiDrift) / (gamma + dt * J.normSquared() / m); | |
impulse.set(J.mult(dt*lambda)); | |
body.applyImpulse(impulse); | |
} | |
}; | |
module.exports = Curve; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Distance',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A constraint that keeps a physics body a given distance away from a given | |
* anchor, or another attached body. | |
* | |
* | |
* @class Distance | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Array} [options.anchor] The location of the anchor | |
* @param {Number} [options.length] The amount of distance from the anchor the constraint should enforce | |
* @param {Number} [options.minLength] The minimum distance before the constraint is activated. Use this property for a "rope" effect. | |
* @param {Number} [options.period] The spring-like reaction when the constraint is broken. | |
* @param {Number} [options.dampingRatio] The damping-like reaction when the constraint is broken. | |
* | |
*/ | |
function Distance(options) { | |
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.impulse = new Vector(); | |
this.normal = new Vector(); | |
this.diffP = new Vector(); | |
this.diffV = new Vector(); | |
Constraint.call(this); | |
} | |
Distance.prototype = Object.create(Constraint.prototype); | |
Distance.prototype.constructor = Distance; | |
Distance.DEFAULT_OPTIONS = { | |
anchor : null, | |
length : 0, | |
minLength : 0, | |
period : 0, | |
dampingRatio : 0 | |
}; | |
/** @const */ var pi = Math.PI; | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Distance.prototype.setOptions = function setOptions(options) { | |
if (options.anchor) { | |
if (options.anchor.position instanceof Vector) this.options.anchor = options.anchor.position; | |
if (options.anchor instanceof Vector) this.options.anchor = options.anchor; | |
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | |
} | |
if (options.length !== undefined) this.options.length = options.length; | |
if (options.dampingRatio !== undefined) this.options.dampingRatio = options.dampingRatio; | |
if (options.period !== undefined) this.options.period = options.period; | |
if (options.minLength !== undefined) this.options.minLength = options.minLength; | |
}; | |
function _calcError(impulse, body) { | |
return body.mass * impulse.norm(); | |
} | |
/** | |
* Set the anchor position | |
* | |
* @method setOptions | |
* @param anchor {Array} | |
*/ | |
Distance.prototype.setAnchor = function setAnchor(anchor) { | |
if (!this.options.anchor) this.options.anchor = new Vector(); | |
this.options.anchor.set(anchor); | |
}; | |
/** | |
* Adds an impulse to a physics body's velocity due to the constraint | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply the constraint to | |
* @param source {Body} The source of the constraint | |
* @param dt {Number} Delta time | |
*/ | |
Distance.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
var n = this.normal; | |
var diffP = this.diffP; | |
var diffV = this.diffV; | |
var impulse = this.impulse; | |
var options = this.options; | |
var dampingRatio = options.dampingRatio; | |
var period = options.period; | |
var minLength = options.minLength; | |
var p2; | |
var w2; | |
if (source) { | |
var v2 = source.velocity; | |
p2 = source.position; | |
w2 = source.inverseMass; | |
} | |
else { | |
p2 = this.options.anchor; | |
w2 = 0; | |
} | |
var length = this.options.length; | |
for (var i = 0; i < targets.length; i++) { | |
var body = targets[i]; | |
var v1 = body.velocity; | |
var p1 = body.position; | |
var w1 = body.inverseMass; | |
diffP.set(p1.sub(p2)); | |
n.set(diffP.normalize()); | |
var dist = diffP.norm() - length; | |
//rope effect | |
if (Math.abs(dist) < minLength) return; | |
if (source) diffV.set(v1.sub(v2)); | |
else diffV.set(v1); | |
var effMass = 1 / (w1 + w2); | |
var gamma; | |
var beta; | |
if (period === 0) { | |
gamma = 0; | |
beta = 1; | |
} | |
else { | |
var c = 4 * effMass * pi * dampingRatio / period; | |
var k = 4 * effMass * pi * pi / (period * period); | |
gamma = 1 / (c + dt*k); | |
beta = dt*k / (c + dt*k); | |
} | |
var antiDrift = beta/dt * dist; | |
var lambda = -(n.dot(diffV) + antiDrift) / (gamma + dt/effMass); | |
impulse.set(n.mult(dt*lambda)); | |
body.applyImpulse(impulse); | |
if (source) source.applyImpulse(impulse.mult(-1)); | |
} | |
}; | |
module.exports = Distance; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Surface',['require','exports','module','./Constraint','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A constraint that keeps a physics body on a given implicit surface | |
* regardless of other physical forces are applied to it. | |
* | |
* @class Surface | |
* @constructor | |
* @extends Constraint | |
* @param {Options} [options] An object of configurable options. | |
* @param {Function} [options.equation] An implicitly defined surface f(x,y,z) = 0 that body is constrained to e.g. function(x,y,z) { x*x + y*y + z*z - r*r } corresponds to a sphere of radius r pixels. | |
* @param {Number} [options.period] The spring-like reaction when the constraint is violated. | |
* @param {Number} [options.dampingRatio] The damping-like reaction when the constraint is violated. | |
*/ | |
function Surface(options) { | |
this.options = Object.create(Surface.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
this.J = new Vector(); | |
this.impulse = new Vector(); | |
Constraint.call(this); | |
} | |
Surface.prototype = Object.create(Constraint.prototype); | |
Surface.prototype.constructor = Surface; | |
Surface.DEFAULT_OPTIONS = { | |
equation : undefined, | |
period : 0, | |
dampingRatio : 0 | |
}; | |
/** @const */ var epsilon = 1e-7; | |
/** @const */ var pi = Math.PI; | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Surface.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds a surface impulse to a physics body. | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply force to. | |
* @param source {Body} Not applicable | |
* @param dt {Number} Delta time | |
*/ | |
Surface.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
var impulse = this.impulse; | |
var J = this.J; | |
var options = this.options; | |
var f = options.equation; | |
var dampingRatio = options.dampingRatio; | |
var period = options.period; | |
for (var i = 0; i < targets.length; i++) { | |
var particle = targets[i]; | |
var v = particle.velocity; | |
var p = particle.position; | |
var m = particle.mass; | |
var gamma; | |
var beta; | |
if (period === 0) { | |
gamma = 0; | |
beta = 1; | |
} | |
else { | |
var c = 4 * m * pi * dampingRatio / period; | |
var k = 4 * m * pi * pi / (period * period); | |
gamma = 1 / (c + dt*k); | |
beta = dt*k / (c + dt*k); | |
} | |
var x = p.x; | |
var y = p.y; | |
var z = p.z; | |
var f0 = f(x, y, z); | |
var dfx = (f(x + epsilon, p, p) - f0) / epsilon; | |
var dfy = (f(x, y + epsilon, p) - f0) / epsilon; | |
var dfz = (f(x, y, p + epsilon) - f0) / epsilon; | |
J.setXYZ(dfx, dfy, dfz); | |
var antiDrift = beta/dt * f0; | |
var lambda = -(J.dot(v) + antiDrift) / (gamma + dt * J.normSquared() / m); | |
impulse.set(J.mult(dt*lambda)); | |
particle.applyImpulse(impulse); | |
} | |
}; | |
module.exports = Surface; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/constraints/Walls',['require','exports','module','./Constraint','./Wall','famous/math/Vector'],function(require, exports, module) { | |
var Constraint = require('./Constraint'); | |
var Wall = require('./Wall'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* Walls combines one or more Wall primitives and exposes a simple API to | |
* interact with several walls at once. A common use case would be to set up | |
* a bounding box for a physics body, that would collide with each side. | |
* | |
* @class Walls | |
* @constructor | |
* @extends Constraint | |
* @uses Wall | |
* @param {Options} [options] An object of configurable options. | |
* @param {Array} [options.sides] An array of sides e.g., [Walls.LEFT, Walls.TOP] | |
* @param {Array} [options.size] The size of the bounding box of the walls. | |
* @param {Array} [options.origin] The center of the wall relative to the size. | |
* @param {Array} [options.drift] Baumgarte stabilization parameter. Makes constraints "loosely" (0) or "tightly" (1) enforced. Range : [0, 1] | |
* @param {Array} [options.slop] Amount of penetration in pixels to ignore before collision event triggers. | |
* @param {Array} [options.restitution] The energy ratio lost in a collision (0 = stick, 1 = elastic) The energy ratio lost in a collision (0 = stick, 1 = elastic) | |
* @param {Array} [options.onContact] How to handle collision against the wall. | |
*/ | |
function Walls(options) { | |
this.options = Object.create(Walls.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
_createComponents.call(this, options.sides || this.options.sides); | |
Constraint.call(this); | |
} | |
Walls.prototype = Object.create(Constraint.prototype); | |
Walls.prototype.constructor = Walls; | |
/** | |
* @property Walls.ON_CONTACT | |
* @type Object | |
* @extends Wall.ON_CONTACT | |
* @static | |
*/ | |
Walls.ON_CONTACT = Wall.ON_CONTACT; | |
/** | |
* An enumeration of common types of walls | |
* LEFT, RIGHT, TOP, BOTTOM, FRONT, BACK | |
* TWO_DIMENSIONAL, THREE_DIMENSIONAL | |
* | |
* @property Walls.SIDES | |
* @type Object | |
* @final | |
* @static | |
*/ | |
Walls.SIDES = { | |
LEFT : 0, | |
RIGHT : 1, | |
TOP : 2, | |
BOTTOM : 3, | |
FRONT : 4, | |
BACK : 5, | |
TWO_DIMENSIONAL : [0, 1, 2, 3], | |
THREE_DIMENSIONAL : [0, 1, 2, 3, 4, 5] | |
}; | |
Walls.DEFAULT_OPTIONS = { | |
sides : Walls.SIDES.TWO_DIMENSIONAL, | |
size : [window.innerWidth, window.innerHeight, 0], | |
origin : [.5, .5, .5], | |
drift : 0.5, | |
slop : 0, | |
restitution : 0.5, | |
onContact : Walls.ON_CONTACT.REFLECT | |
}; | |
var _SIDE_NORMALS = { | |
0 : new Vector(1, 0, 0), | |
1 : new Vector(-1, 0, 0), | |
2 : new Vector(0, 1, 0), | |
3 : new Vector(0,-1, 0), | |
4 : new Vector(0, 0, 1), | |
5 : new Vector(0, 0,-1) | |
}; | |
function _getDistance(side, size, origin) { | |
var distance; | |
var SIDES = Walls.SIDES; | |
switch (parseInt(side)) { | |
case SIDES.LEFT: | |
distance = size[0] * origin[0]; | |
break; | |
case SIDES.TOP: | |
distance = size[1] * origin[1]; | |
break; | |
case SIDES.FRONT: | |
distance = size[2] * origin[2]; | |
break; | |
case SIDES.RIGHT: | |
distance = size[0] * (1 - origin[0]); | |
break; | |
case SIDES.BOTTOM: | |
distance = size[1] * (1 - origin[1]); | |
break; | |
case SIDES.BACK: | |
distance = size[2] * (1 - origin[2]); | |
break; | |
} | |
return distance; | |
} | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Walls.prototype.setOptions = function setOptions(options) { | |
var resizeFlag = false; | |
if (options.restitution !== undefined) _setOptionsForEach.call(this, {restitution : options.restitution}); | |
if (options.drift !== undefined) _setOptionsForEach.call(this, {drift : options.drift}); | |
if (options.slop !== undefined) _setOptionsForEach.call(this, {slop : options.slop}); | |
if (options.onContact !== undefined) _setOptionsForEach.call(this, {onContact : options.onContact}); | |
if (options.size !== undefined) resizeFlag = true; | |
if (options.sides !== undefined) this.options.sides = options.sides; | |
if (options.origin !== undefined) resizeFlag = true; | |
if (resizeFlag) this.setSize(options.size, options.origin); | |
}; | |
function _createComponents(sides) { | |
this.components = {}; | |
var components = this.components; | |
for (var i = 0; i < sides.length; i++) { | |
var side = sides[i]; | |
components[i] = new Wall({ | |
normal : _SIDE_NORMALS[side].clone(), | |
distance : _getDistance(side, this.options.size, this.options.origin) | |
}); | |
} | |
} | |
/* | |
* Setter for size. | |
* | |
* @method setOptions | |
* @param options {Objects} | |
*/ | |
Walls.prototype.setSize = function setSize(size, origin) { | |
origin = origin || this.options.origin; | |
if (origin.length < 3) origin[2] = 0.5; | |
this.forEach(function(wall, side) { | |
var d = _getDistance(side, size, origin); | |
wall.setOptions({distance : d}); | |
}); | |
this.options.size = size; | |
this.options.origin = origin; | |
}; | |
function _setOptionsForEach(options) { | |
this.forEach(function(wall) { | |
wall.setOptions(options); | |
}); | |
for (var key in options) this.options[key] = options[key]; | |
} | |
/** | |
* Adds an impulse to a physics body's velocity due to the walls constraint | |
* | |
* @method applyConstraint | |
* @param targets {Array.Body} Array of bodies to apply the constraint to | |
* @param source {Body} The source of the constraint | |
* @param dt {Number} Delta time | |
*/ | |
Walls.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | |
this.forEach(function(wall) { | |
wall.applyConstraint(targets, source, dt); | |
}); | |
}; | |
/** | |
* Apply a method to each wall making up the walls | |
* | |
* @method applyConstraint | |
* @param fn {Function} Function that takes in a wall as its first parameter | |
*/ | |
Walls.prototype.forEach = function forEach(fn) { | |
for (var key in this.sides) fn(this.sides[key], key); | |
}; | |
/** | |
* Rotates the walls by an angle in the XY-plane | |
* | |
* @method applyConstraint | |
* @param angle {Function} | |
*/ | |
Walls.prototype.rotateZ = function rotateZ(angle) { | |
this.forEach(function(wall) { | |
var n = wall.options.normal; | |
n.rotateZ(angle).put(n); | |
}); | |
}; | |
/** | |
* Rotates the walls by an angle in the YZ-plane | |
* | |
* @method applyConstraint | |
* @param angle {Function} | |
*/ | |
Walls.prototype.rotateX = function rotateX(angle) { | |
this.forEach(function(wall) { | |
var n = wall.options.normal; | |
n.rotateX(angle).put(n); | |
}); | |
}; | |
/** | |
* Rotates the walls by an angle in the XZ-plane | |
* | |
* @method applyConstraint | |
* @param angle {Function} | |
*/ | |
Walls.prototype.rotateY = function rotateY(angle) { | |
this.forEach(function(wall) { | |
var n = wall.options.normal; | |
n.rotateY(angle).put(n); | |
}); | |
}; | |
module.exports = Walls; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
//TODO: test options manager | |
define('famous/physics/forces/Repulsion',['require','exports','module','./Force','famous/math/Vector'],function(require, exports, module) { | |
var Force = require('./Force'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* Repulsion is a force that repels (attracts) bodies away (towards) | |
* each other. A repulsion of negative strength is attractive. | |
* | |
* @class Repulsion | |
* @constructor | |
* @extends Force | |
* @param {Object} options overwrites default options | |
*/ | |
function Repulsion(options) { | |
this.options = Object.create(Repulsion.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
//registers | |
this.disp = new Vector(); | |
Force.call(this); | |
} | |
Repulsion.prototype = Object.create(Force.prototype); | |
Repulsion.prototype.constructor = Repulsion; | |
/** | |
* @property Repulsion.DECAY_FUNCTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Repulsion.DECAY_FUNCTIONS = { | |
/** | |
* A linear decay function | |
* @attribute LINEAR | |
* @type Function | |
* @param {Number} r distance from the source body | |
* @param {Number} cutoff the effective radius of influence | |
*/ | |
LINEAR : function(r, cutoff) { | |
return Math.max(1 - (1 / cutoff) * r, 0); | |
}, | |
/** | |
* A Morse potential decay function (http://en.wikipedia.org/wiki/Morse_potential) | |
* @attribute MORSE | |
* @type Function | |
* @param {Number} r distance from the source body | |
* @param {Number} cutoff the minimum radius of influence | |
*/ | |
MORSE : function(r, cutoff) { | |
var r0 = (cutoff === 0) ? 100 : cutoff; | |
var rShifted = r + r0 * (1 - Math.log(2)); //shift by x-intercept | |
return Math.max(1 - Math.pow(1 - Math.exp(rShifted/r0 - 1), 2), 0); | |
}, | |
/** | |
* An inverse distance decay function | |
* @attribute INVERSE | |
* @type Function | |
* @param {Number} r distance from the source body | |
* @param {Number} cutoff a distance shift to avoid singularities | |
*/ | |
INVERSE : function(r, cutoff) { | |
return 1 / (1 - cutoff + r); | |
}, | |
/** | |
* An inverse squared distance decay function | |
* @attribute INVERSE | |
* @type Function | |
* @param {Number} r distance from the source body | |
* @param {Number} cutoff a distance shift to avoid singularities | |
*/ | |
GRAVITY : function(r, cutoff) { | |
return 1 / (1 - cutoff + r*r); | |
} | |
}; | |
/** | |
* @property Repulsion.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
Repulsion.DEFAULT_OPTIONS = { | |
/** | |
* The strength of the force | |
* Range : [0, 100] | |
* @attribute strength | |
* @type Number | |
* @default 1 | |
*/ | |
strength : 1, | |
/** | |
* The location of the force, if not another physics body | |
* | |
* @attribute anchor | |
* @type Number | |
* @default 0.01 | |
* @optional | |
*/ | |
anchor : undefined, | |
/** | |
* The range of the repulsive force | |
* @attribute radii | |
* @type Array | |
* @default [0, Infinity] | |
*/ | |
range : [0, Infinity], | |
/** | |
* A normalization for the force to avoid singularities at the origin | |
* @attribute cutoff | |
* @type Number | |
* @default 0 | |
*/ | |
cutoff : 0, | |
/** | |
* The maximum magnitude of the force | |
* Range : [0, Infinity] | |
* @attribute cap | |
* @type Number | |
* @default Infinity | |
*/ | |
cap : Infinity, | |
/** | |
* The type of decay the repulsive force should have | |
* @attribute decayFunction | |
* @type Function | |
*/ | |
decayFunction : Repulsion.DECAY_FUNCTIONS.GRAVITY | |
}; | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param {Objects} options | |
*/ | |
Repulsion.prototype.setOptions = function setOptions(options) { | |
if (options.anchor !== undefined) { | |
if (options.anchor.position instanceof Vector) this.options.anchor = options.anchor.position; | |
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | |
delete options.anchor; | |
} | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
/** | |
* Adds a drag force to a physics body's force accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.Body} Array of bodies to apply force to | |
* @param source {Body} The source of the force | |
*/ | |
Repulsion.prototype.applyForce = function applyForce(targets, source) { | |
var options = this.options; | |
var force = this.force; | |
var disp = this.disp; | |
var strength = options.strength; | |
var anchor = options.anchor || source.position; | |
var cap = options.cap; | |
var cutoff = options.cutoff; | |
var rMin = options.range[0]; | |
var rMax = options.range[1]; | |
var decayFn = options.decayFunction; | |
if (strength === 0) return; | |
for (var index in targets) { | |
var particle = targets[index]; | |
if (particle === source) continue; | |
var m1 = particle.mass; | |
var p1 = particle.position; | |
disp.set(p1.sub(anchor)); | |
var r = disp.norm(); | |
if (r < rMax && r > rMin) { | |
force.set(disp.normalize(strength * m1 * decayFn(r, cutoff)).cap(cap)); | |
particle.applyForce(force); | |
} | |
} | |
}; | |
module.exports = Repulsion; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/forces/RotationalDrag',['require','exports','module','./Drag'],function(require, exports, module) { | |
var Drag = require('./Drag'); | |
/** | |
* Rotational drag is a force that opposes angular velocity. | |
* Attach it to a physics body to slow down its rotation. | |
* | |
* @class RotationalDrag | |
* @constructor | |
* @extends Force | |
* @param {Object} options options to set on drag | |
*/ | |
function RotationalDrag(options) { | |
Drag.call(this, options); | |
} | |
RotationalDrag.prototype = Object.create(Drag.prototype); | |
RotationalDrag.prototype.constructor = RotationalDrag; | |
RotationalDrag.DEFAULT_OPTIONS = Drag.DEFAULT_OPTIONS; | |
RotationalDrag.FORCE_FUNCTIONS = Drag.FORCE_FUNCTIONS; | |
/** | |
* @property Repulsion.FORCE_FUNCTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
RotationalDrag.FORCE_FUNCTIONS = { | |
/** | |
* A drag force proprtional to the angular velocity | |
* @attribute LINEAR | |
* @type Function | |
* @param {Vector} angularVelocity | |
* @return {Vector} drag force | |
*/ | |
LINEAR : function(angularVelocity) { | |
return angularVelocity; | |
}, | |
/** | |
* A drag force proprtional to the square of the angular velocity | |
* @attribute QUADRATIC | |
* @type Function | |
* @param {Vector} angularVelocity | |
* @return {Vector} drag force | |
*/ | |
QUADRATIC : function(angularVelocity) { | |
return angularVelocity.mult(angularVelocity.norm()); | |
} | |
}; | |
/** | |
* Adds a rotational drag force to a physics body's torque accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.Body} Array of bodies to apply drag force to. | |
*/ | |
RotationalDrag.prototype.applyForce = function applyForce(targets) { | |
var strength = this.options.strength; | |
var forceFunction = this.options.forceFunction; | |
var force = this.force; | |
//TODO: rotational drag as function of inertia | |
for (var index = 0; index < targets.length; index++) { | |
var particle = targets[index]; | |
forceFunction(particle.angularVelocity).mult(-100*strength).put(force); | |
particle.applyTorque(force); | |
} | |
}; | |
/* | |
* Setter for options. | |
* | |
* @method setOptions | |
* @param {Objects} options | |
*/ | |
RotationalDrag.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
module.exports = RotationalDrag; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
//TODO: test inheritance | |
define('famous/physics/forces/RotationalSpring',['require','exports','module','./Spring'],function(require, exports, module) { | |
var Spring = require('./Spring'); | |
/** | |
* A force that rotates a physics body back to target Euler angles. | |
* Just as a spring translates a body to a particular X, Y, Z, location, | |
* a rotational spring rotates a body to a particular X, Y, Z Euler angle. | |
* Note: there is no physical agent that does this in the "real world" | |
* | |
* @class RotationalSpring | |
* @constructor | |
* @extends Spring | |
* @param {Object} options options to set on drag | |
*/ | |
function RotationalSpring(options) { | |
Spring.call(this, options); | |
} | |
RotationalSpring.prototype = Object.create(Spring.prototype); | |
RotationalSpring.prototype.constructor = RotationalSpring; | |
RotationalSpring.DEFAULT_OPTIONS = Spring.DEFAULT_OPTIONS; | |
RotationalSpring.FORCE_FUNCTIONS = Spring.FORCE_FUNCTIONS; | |
/** | |
* Adds a torque force to a physics body's torque accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.Body} Array of bodies to apply torque to. | |
*/ | |
RotationalSpring.prototype.applyForce = function applyForce(targets) { | |
var force = this.force; | |
var options = this.options; | |
var disp = this.disp; | |
var stiffness = options.stiffness; | |
var damping = options.damping; | |
var restLength = options.length; | |
var anchor = options.anchor; | |
for (var i = 0; i < targets.length; i++) { | |
var target = targets[i]; | |
disp.set(anchor.sub(target.orientation)); | |
var dist = disp.norm() - restLength; | |
if (dist === 0) return; | |
//if dampingRatio specified, then override strength and damping | |
var m = target.mass; | |
stiffness *= m; | |
damping *= m; | |
force.set(disp.normalize(stiffness * this.forceFunction(dist, this.options.lMax))); | |
if (damping) force.set(force.add(target.angularVelocity.mult(-damping))); | |
target.applyTorque(force); | |
} | |
}; | |
/** | |
* Calculates the potential energy of the rotational spring. | |
* | |
* @method getEnergy | |
* @param {Body} target The physics body attached to the spring | |
*/ | |
RotationalSpring.prototype.getEnergy = function getEnergy(target) { | |
var options = this.options; | |
var restLength = options.length; | |
var anchor = options.anchor; | |
var strength = options.stiffness; | |
var dist = anchor.sub(target.orientation).norm() - restLength; | |
return 0.5 * strength * dist * dist; | |
}; | |
module.exports = RotationalSpring; | |
}); | |
/* 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: [email protected] | |
* @license MPL 2.0 | |
* @copyright Famous Industries, Inc. 2014 | |
*/ | |
define('famous/physics/forces/VectorField',['require','exports','module','./Force','famous/math/Vector'],function(require, exports, module) { | |
var Force = require('./Force'); | |
var Vector = require('famous/math/Vector'); | |
/** | |
* A force that moves a physics body to a location with a spring motion. | |
* The body can be moved to another physics body, or an anchor point. | |
* | |
* @class VectorField | |
* @constructor | |
* @extends Force | |
* @param {Object} options options to set on drag | |
*/ | |
function VectorField(options) { | |
this.options = Object.create(VectorField.DEFAULT_OPTIONS); | |
if (options) this.setOptions(options); | |
_setFieldOptions.call(this, this.options.field); | |
Force.call(this); | |
//registers | |
this.evaluation = new Vector(0,0,0); | |
} | |
VectorField.prototype = Object.create(Force.prototype); | |
VectorField.prototype.constructor = VectorField; | |
/** | |
* @property Spring.FORCE_FUNCTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
VectorField.FIELDS = { | |
/** | |
* Constant force, e.g., gravity | |
* @attribute CONSTANT | |
* @type Function | |
* @param v {Vector} Current position of physics body | |
* @param options {Object} The direction of the force | |
* Pass a {direction : Vector} into the VectorField options | |
* @return {Number} unscaled force | |
*/ | |
CONSTANT : function(v, options) { | |
return v.set(options.direction); | |
}, | |
/** | |
* Linear force | |
* @attribute LINEAR | |
* @type Function | |
* @param v {Vector} Current position of physics body | |
* @return {Number} unscaled force | |
*/ | |
LINEAR : function(v) { | |
return v; | |
}, | |
/** | |
* Radial force, e.g., Hookean spring | |
* @attribute RADIAL | |
* @type Function | |
* @param v {Vector} Current position of physics body | |
* @return {Number} unscaled force | |
*/ | |
RADIAL : function(v) { | |
return v.set(v.mult(-1, v)); | |
}, | |
/** | |
* Spherical force | |
* @attribute SPHERE_ATTRACTOR | |
* @type Function | |
* @param v {Vector} Current position of physics body | |
* @param options {Object} An object with the radius of the sphere | |
* Pass a {radius : Number} into the VectorField options | |
* @return {Number} unscaled force | |
*/ | |
SPHERE_ATTRACTOR : function(v, options) { | |
return v.set(v.mult((options.radius - v.norm()) / v.norm())); | |
}, | |
/** | |
* Point attractor force, e.g., Hookean spring with an anchor | |
* @attribute POINT_ATTRACTOR | |
* @type Function | |
* @param v {Vector} Current position of physics body | |
* @param options {Object} And object with the position of the attractor | |
* Pass a {position : Vector} into the VectorField options | |
* @return {Number} unscaled force | |
*/ | |
POINT_ATTRACTOR : function(v, options) { | |
return v.set(options.position.sub(v)); | |
} | |
}; | |
/** | |
* @property VectorField.DEFAULT_OPTIONS | |
* @type Object | |
* @protected | |
* @static | |
*/ | |
VectorField.DEFAULT_OPTIONS = { | |
/** | |
* The strength of the force | |
* Range : [0, 10] | |
* @attribute strength | |
* @type Number | |
* @default 1 | |
*/ | |
strength : 1, | |
/** | |
* Type of vectorfield | |
* Range : [0, 100] | |
* @attribute field | |
* @type Function | |
*/ | |
field : VectorField.FIELDS.CONSTANT | |
}; | |
/** | |
* Basic options setter | |
* | |
* @method setOptions | |
* @param {Objects} options | |
*/ | |
VectorField.prototype.setOptions = function setOptions(options) { | |
for (var key in options) this.options[key] = options[key]; | |
}; | |
function _setFieldOptions(field) { | |
var FIELDS = VectorField.FIELDS; | |
switch (field) { | |
case FIELDS.CONSTANT: | |
if (!this.options.direction) this.options.direction = new Vector(0,1,0); | |
break; | |
case FIELDS.POINT_ATTRACTOR: | |
if (!this.options.position) this.options.position = new Vector(0,0,0); | |
break; | |
case FIELDS.SPHERE_ATTRACTOR: | |
if (!this.options.radius) this.options.radius = 1; | |
break; | |
} | |
} | |
function _evaluate(v) { | |
var evaluation = this.evaluation; | |
var field = this.options.field; | |
evaluation.set(v); | |
return field(evaluation, this.options); | |
} | |
/** | |
* Adds the vectorfield's force to a physics body's force accumulator. | |
* | |
* @method applyForce | |
* @param targets {Array.body} Array of bodies to apply force to. | |
*/ | |
VectorField.prototype.applyForce = function applyForce(targets) { | |
var force = this.force; | |
for (var i = 0; i < targets.length; i++) { | |
var particle = targets[i]; | |
force.set( | |
_evaluate.call(this, particle.position) | |
.mult(particle.mass * this.options.strength) | |
); | |
particle.applyForce(force); | |
} | |
}; | |
module.exports = VectorField; | |
}); | |
(function(){ | |
function loadcss(filename) { | |
var fileref = document.createElement('link'); | |
fileref.setAttribute('rel', 'stylesheet'); | |
fileref.setAttribute('type', 'text/css'); | |
fileref.setAttribute('href', filename); | |
document.getElementsByTagName('head')[0].appendChild(fileref); | |
} | |
loadcss('http://code.famo.us/famous/0.2/famous.css'); | |
window.addEventListener('load',function(){ | |
console.log('----------- start -----------'); | |
require(['main']); | |
}); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if(!Function.prototype.bind){Function.prototype.bind=function(oThis){if(typeof this!=="function"){throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable")}var aArgs=Array.prototype.slice.call(arguments,1),fToBind=this,fNOP=function(){},fBound=function(){return fToBind.apply(this instanceof fNOP&&oThis?this:oThis,aArgs.concat(Array.prototype.slice.call(arguments)))};fNOP.prototype=this.prototype;fBound.prototype=new fNOP;return fBound}}if(typeof document!=="undefined"&&!("classList"in document.createElement("a"))){(function(view){"use strict";var classListProp="classList",protoProp="prototype",elemCtrProto=(view.HTMLElement||view.Element)[protoProp],objCtr=Object,strTrim=String[protoProp].trim||function(){return this.replace(/^\s+|\s+$/g,"")},arrIndexOf=Array[protoProp].indexOf||function(item){var i=0,len=this.length;for(;i<len;i++){if(i in this&&this[i]===item){return i}}return-1},DOMEx=function(type,message){this.name=type;this.code=DOMException[type];this.message=message},checkTokenAndGetIndex=function(classList,token){if(token===""){throw new DOMEx("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(token)){throw new DOMEx("INVALID_CHARACTER_ERR","String contains an invalid character")}return arrIndexOf.call(classList,token)},ClassList=function(elem){var trimmedClasses=strTrim.call(elem.className),classes=trimmedClasses?trimmedClasses.split(/\s+/):[],i=0,len=classes.length;for(;i<len;i++){this.push(classes[i])}this._updateClassName=function(){elem.className=this.toString()}},classListProto=ClassList[protoProp]=[],classListGetter=function(){return new ClassList(this)};DOMEx[protoProp]=Error[protoProp];classListProto.item=function(i){return this[i]||null};classListProto.contains=function(token){token+="";return checkTokenAndGetIndex(this,token)!==-1};classListProto.add=function(token){token+="";if(checkTokenAndGetIndex(this,token)===-1){this.push(token);this._updateClassName()}};classListProto.remove=function(token){token+="";var index=checkTokenAndGetIndex(this,token);if(index!==-1){this.splice(index,1);this._updateClassName()}};classListProto.toggle=function(token){token+="";if(checkTokenAndGetIndex(this,token)===-1){this.add(token)}else{this.remove(token)}};classListProto.toString=function(){return this.join(" ")};if(objCtr.defineProperty){var classListPropDesc={get:classListGetter,enumerable:true,configurable:true};try{objCtr.defineProperty(elemCtrProto,classListProp,classListPropDesc)}catch(ex){if(ex.number===-2146823252){classListPropDesc.enumerable=false;objCtr.defineProperty(elemCtrProto,classListProp,classListPropDesc)}}}else if(objCtr[protoProp].__defineGetter__){elemCtrProto.__defineGetter__(classListProp,classListGetter)}})(self)}window.requestAnimationFrame||(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback,element){return window.setTimeout(function(){callback(+new Date)},1e3/60)});var requirejs,require,define;(function(ca){function G(b){return"[object Function]"===M.call(b)}function H(b){return"[object Array]"===M.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function U(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function j(b,c){return s(b,c)&&b[c]}function B(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function V(b,c,d,g){c&&B(c,function(c,h){if(d||!s(b,h))g&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof RegExp)?(b[h]||(b[h]={}),V(b[h],c,d,g)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function da(b){throw b}function ea(b){if(!b)return b;var c=ca;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ha(b){function c(a,e,b){var f,n,c,d,g,h,i,I=e&&e.split("/");n=I;var m=l.map,k=m&&m["*"];if(a&&"."===a.charAt(0))if(e){n=I.slice(0,I.length-1);a=a.split("/");e=a.length-1;l.nodeIdCompat&&R.test(a[e])&&(a[e]=a[e].replace(R,""));n=a=n.concat(a);d=n.length;for(e=0;e<d;e++)if(c=n[e],"."===c)n.splice(e,1),e-=1;else if(".."===c)if(1===e&&(".."===n[2]||".."===n[0]))break;else 0<e&&(n.splice(e-1,2),e-=2);a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&m&&(I||k)){n=a.split("/");e=n.length;a:for(;0<e;e-=1){d=n.slice(0,e).join("/");if(I)for(c=I.length;0<c;c-=1)if(b=j(m,I.slice(0,c).join("/")))if(b=j(b,d)){f=b;g=e;break a}!h&&(k&&j(k,d))&&(h=j(k,d),i=e)}!f&&h&&(f=h,g=i);f&&(n.splice(0,g,f),a=n.join("/"))}return(f=j(l.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(e){if(e.getAttribute("data-requiremodule")===a&&e.getAttribute("data-requirecontext")===i.contextName)return e.parentNode.removeChild(e),!0})}function g(a){var e=j(l.paths,a);if(e&&H(e)&&1<e.length)return e.shift(),i.require.undef(a),i.require([a]),!0}function u(a){var e,b=a?a.indexOf("!"):-1;-1<b&&(e=a.substring(0,b),a=a.substring(b+1,a.length));return[e,a]}function m(a,e,b,f){var n,d,g=null,h=e?e.name:null,l=a,m=!0,k="";a||(m=!1,a="_@r"+(M+=1));a=u(a);g=a[0];a=a[1];g&&(g=c(g,h,f),d=j(p,g));a&&(g?k=d&&d.normalize?d.normalize(a,function(a){return c(a,h,f)}):c(a,h,f):(k=c(a,h,f),a=u(k),g=a[0],k=a[1],b=!0,n=i.nameToUrl(k)));b=g&&!d&&!b?"_unnormalized"+(Q+=1):"";return{prefix:g,name:k,parentMap:e,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(g?g+"!"+k:k)+b}}function q(a){var e=a.id,b=j(k,e);b||(b=k[e]=new i.Module(a));return b}function r(a,e,b){var f=a.id,n=j(k,f);if(s(p,f)&&(!n||n.defineEmitComplete))"defined"===e&&b(p[f]);else if(n=q(a),n.error&&"error"===e)b(n.error);else n.on(e,b)}function w(a,e){var b=a.requireModules,f=!1;if(e)e(a);else if(v(b,function(e){if(e=j(k,e))e.error=a,e.events.error&&(f=!0,e.emit("error",a))}),!f)h.onError(a)}function x(){S.length&&(ia.apply(A,[A.length,0].concat(S)),S=[])}function y(a){delete k[a];delete W[a]}function F(a,e,b){var f=a.map.id;a.error?a.emit("error",a.error):(e[f]=!0,v(a.depMaps,function(f,c){var d=f.id,g=j(k,d);g&&(!a.depMatched[c]&&!b[d])&&(j(e,d)?(a.defineDep(c,p[d]),a.check()):F(g,e,b))}),b[f]=!0)}function D(){var a,e,b=(a=1e3*l.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],c=[],h=!1,k=!0;if(!X){X=!0;B(W,function(a){var i=a.map,m=i.id;if(a.enabled&&(i.isDefine||c.push(a),!a.error))if(!a.inited&&b)g(m)?h=e=!0:(f.push(m),d(m));else if(!a.inited&&(a.fetched&&i.isDefine)&&(h=!0,!i.prefix))return k=!1});if(b&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,f),a.contextName=i.contextName,w(a);k&&v(c,function(a){F(a,{},{})});if((!b||e)&&h)if((z||fa)&&!Y)Y=setTimeout(function(){Y=0;D()},50);X=!1}}function E(a){s(p,a[0])||q(m(a[0],null,!0)).init(a[1],a[2])}function K(a){var a=a.currentTarget||a.srcElement,e=i.onScriptLoad;a.detachEvent&&!Z?a.detachEvent("onreadystatechange",e):a.removeEventListener("load",e,!1);e=i.onScriptError;(!a.detachEvent||Z)&&a.removeEventListener("error",e,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function L(){var a;for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var X,$,i,N,Y,l={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},k={},W={},aa={},A=[],p={},T={},ba={},M=1,Q=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?p[a.map.id]=a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return j(l.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};$=function(a){this.events=j(aa,a.id)||{};this.map=a;this.shim=j(l.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};$.prototype={init:function(a,e,b,f){f=f||{};if(!this.inited){this.factory=e;if(b)this.on("error",b);else this.events.error&&(b=t(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=b;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,e){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=e)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=this.map.url;T[a]||(T[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,e,b=this.map.id;e=this.depExports;var f=this.exports,c=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&&(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f);if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval","fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b,a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m,nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild=!0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==d&&(!("."===g||".."===g)||1<d))e=b.substring(d,b.length),b=b.substring(0,d);return i.nameToUrl(c(b,a&&a.id,!0),e,!0)},defined:function(b){return s(p,m(b,a,!1,!0).id)},specified:function(b){b=m(b,a,!1,!0).id;return s(p,b)||s(k,b)}});a||(g.undef=function(b){x();var c=m(b,a,!0),e=j(k,b);d(b);delete p[b];delete T[c.url];delete aa[b];U(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&(aa[b]=e.events),y(b))});return g},enable:function(a){j(k,a.id)&&q(a).enable()},completeLoad:function(a){var b,c,f=j(l.shim,a)||{},d=f.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=j(k,a);if(!b&&!s(p,a)&&c&&!c.inited){if(l.enforceDefine&&(!d||!ea(d)))return g(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,f.deps||[],f.exportsFn])}D()},nameToUrl:function(a,b,c){var f,d,g;(f=j(l.pkgs,a))&&(a=f);if(f=j(ba,a))return i.nameToUrl(f,b,c);if(h.jsExtRegExp.test(a))f=a+(b||"");else{f=l.paths;a=a.split("/");for(d=a.length;0<d;d-=1)if(g=a.slice(0,d).join("/"),g=j(f,g)){H(g)&&(g=g[0]);a.splice(0,d,g);break}f=a.join("/");f+=b||(/^data\:|\?/.test(f)||c?"":".js");f=("/"===f.charAt(0)||f.match(/^[\w\+\.\-]+:/)?"":l.baseUrl)+f}return l.urlArgs?f+((-1===f.indexOf("?")?"?":"&")+l.urlArgs):f},load:function(a,b){h.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=K(a),i.completeLoad(a.id)},onScriptError:function(a){var b=K(a);if(!g(b.id))return w(C("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var h,x,y,D,K,E,P,L,q,Q,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/gm,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,R=/\.js$/,ja=/^\.\//;x=Object.prototype;var M=x.toString,ga=x.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),fa=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,Z="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},r={},S=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;r=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(r=require,require=void 0);h=requirejs=function(b,c,d,g){var u,m="_";!H(b)&&"string"!==typeof b&&(u=b,H(c)?(b=c,c=d,d=g):b=[]);u&&u.context&&(m=u.context);(g=j(F,m))||(g=F[m]=h.s.newContext(m));u&&g.configure(u);return g.require(b,c,d)};h.config=function(b){return h(b)};h.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=h);h.version="2.1.11";h.jsExtRegExp=/^\/|:|\?|\.js$/;h.isBrowser=z;x=h.s={contexts:F,newContext:ha};h({});v(["toUrl","undef","defined","specified"],function(b){h[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;h.onError=da;h.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};h.load=function(b,c,d){var g=b&&b.config||{};if(z)return g=h.createNode(g,c,d),g.setAttribute("data-requirecontext",b.contextName),g.setAttribute("data-requiremodule",c),g.attachEvent&&!(g.attachEvent.toString&&0>g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl=Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b||(b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this);define("famous/core/Entity",["require","exports","module"],function(require,exports,module){var entities=[];function get(id){return entities[id]}function set(id,entity){entities[id]=entity}function register(entity){var id=entities.length;set(id,entity);return id}function unregister(id){set(id,null)}module.exports={register:register,unregister:unregister,get:get,set:set}});define("famous/core/Transform",["require","exports","module"],function(require,exports,module){var Transform={};Transform.precision=1e-6;Transform.identity=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];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]]};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]};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]};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])};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]};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]};Transform.scale=function scale(x,y,z){if(z===undefined)z=1;return[x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1]};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]};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]};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]};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};Transform.rotateAxis=function rotateAxis(v,theta){var sinTheta=Math.sin(theta);var cosTheta=Math.cos(theta);var verTheta=1-cosTheta;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};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])};Transform.skew=function skew(phi,theta,psi){return[1,0,0,0,Math.tan(psi),1,0,0,Math.tan(theta),Math.tan(phi),1,0,0,0,0,1]};Transform.skewX=function skewX(angle){return[1,0,0,0,Math.tan(angle),1,0,0,0,0,1,0,0,0,0,1]};Transform.skewY=function skewY(angle){return[1,Math.tan(angle),0,0,0,1,0,0,0,0,1,0,0,0,0,1]};Transform.perspective=function perspective(focusZ){return[1,0,0,0,0,1,0,0,0,0,1,-1/focusZ,0,0,0,1]};Transform.getTranslate=function getTranslate(m){return[m[12],m[13],m[14]]};Transform.inverse=function inverse(m){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};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}Transform.interpret=function interpret(M){var x=[M[0],M[1],M[2]];var sgn=_sign(x[0]);var xNorm=_norm(x);var v=[x[0]+sgn*xNorm,x[1],x[2]];var mult=2/_normSquared(v);if(mult>=Infinity){return{translate:Transform.getTranslate(M),rotate:[0,0,0],scale:[0,0,0],skew:[0,0,0]}}var Q1=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];Q1[0]=1-mult*v[0]*v[0];Q1[5]=1-mult*v[1]*v[1];Q1[10]=1-mult*v[2]*v[2];Q1[1]=-mult*v[0]*v[1];Q1[2]=-mult*v[0]*v[2];Q1[6]=-mult*v[1]*v[2];Q1[4]=Q1[1];Q1[8]=Q1[2];Q1[9]=Q1[6];var MQ1=Transform.multiply(Q1,M);var x2=[MQ1[5],MQ1[6]];var sgn2=_sign(x2[0]);var x2Norm=_norm(x2);var v2=[x2[0]+sgn2*x2Norm,x2[1]];var mult2=2/_normSquared(v2);var Q2=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];Q2[5]=1-mult2*v2[0]*v2[0];Q2[10]=1-mult2*v2[1]*v2[1];Q2[6]=-mult2*v2[0]*v2[1];Q2[9]=Q2[6];var Q=Transform.multiply(Q2,Q1);var R=Transform.multiply(Q,M);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);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])];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};Transform.average=function average(M1,M2,t){t=t===undefined?.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)};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)};Transform.equals=function equals(a,b){return!Transform.notEquals(a,b)};Transform.notEquals=function notEquals(a,b){if(a===b)return false;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]};Transform.normalizeRotation=function normalizeRotation(rotation){var result=rotation.slice(0);if(result[0]===Math.PI*.5||result[0]===-Math.PI*.5){result[0]=-result[0];result[1]=Math.PI-result[1];result[2]-=Math.PI}if(result[0]>Math.PI*.5){result[0]=result[0]-Math.PI;result[1]=Math.PI-result[1];result[2]-=Math.PI}if(result[0]<-Math.PI*.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};Transform.inFront=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,.001,1];Transform.behind=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,-.001,1];module.exports=Transform});define("famous/core/SpecParser",["require","exports","module","./Transform"],function(require,exports,module){var Transform=require("./Transform");function SpecParser(){this.result={}}SpecParser._instance=new SpecParser;SpecParser.parse=function parse(spec,context){return SpecParser._instance.parse(spec,context)};SpecParser.prototype.parse=function parse(spec,context){this.reset();this._parseSpec(spec,context,Transform.identity);return this.result};SpecParser.prototype.reset=function reset(){this.result={}};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 _originZeroZero=[0,0];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||parentContext.origin;if(parentContext.size&&align&&(align[0]||align[1])){var alignAdjust=[align[0]*parentContext.size[0],align[1]*parentContext.size[1],0];transform=Transform.thenMove(transform,_vecInContext(alignAdjust,sizeContext))}this.result[id]={transform:transform,opacity:parentContext.opacity,origin:parentContext.origin||_originZeroZero,align:parentContext.align||parentContext.origin||_originZeroZero,size:parentContext.size}}else if(!spec){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.multiply(parentContext.transform,spec.transform);if(spec.origin){origin=spec.origin;nextSizeContext=parentContext.transform}if(spec.align)align=spec.align;if(spec.size){var parentSize=parentContext.size;size=[spec.size[0]!==undefined?spec.size[0]:parentSize[0],spec.size[1]!==undefined?spec.size[1]:parentSize[1]];if(parentSize){if(!align)align=origin;if(align&&(align[0]||align[1]))transform=Transform.thenMove(transform,_vecInContext([align[0]*parentSize[0],align[1]*parentSize[1],0],sizeContext));if(origin&&(origin[0]||origin[1]))transform=Transform.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)}};module.exports=SpecParser});define("famous/core/RenderNode",["require","exports","module","./Entity","./SpecParser"],function(require,exports,module){var Entity=require("./Entity");var SpecParser=require("./SpecParser");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)}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=[]}else this._child=childNode;return childNode};RenderNode.prototype.get=function get(){return this._object||(this._hasMultipleChildren?null:this._child?this._child.get():null)};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};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};function _applyCommit(spec,context,cacheStorage){var result=SpecParser.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}}RenderNode.prototype.commit=function commit(context){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)};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};module.exports=RenderNode});define("famous/core/EventEmitter",["require","exports","module"],function(require,exports,module){function EventEmitter(){this.listeners={};this._owner=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};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};EventEmitter.prototype.addListener=EventEmitter.prototype.on;EventEmitter.prototype.removeListener=function removeListener(type,handler){var index=this.listeners[type].indexOf(handler);if(index>=0)this.listeners[type].splice(index,1);return this};EventEmitter.prototype.bindThis=function bindThis(owner){this._owner=owner};module.exports=EventEmitter});define("famous/core/EventHandler",["require","exports","module","./EventEmitter"],function(require,exports,module){var EventEmitter=require("./EventEmitter");function EventHandler(){EventEmitter.apply(this,arguments);this.downstream=[];this.downstreamFn=[];this.upstream=[];this.upstreamListeners={}}EventHandler.prototype=Object.create(EventEmitter.prototype);EventHandler.prototype.constructor=EventHandler;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)}};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)};EventHandler.prototype.emit=function emit(type,event){EventEmitter.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};EventHandler.prototype.trigger=EventHandler.prototype.emit;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};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};EventHandler.prototype.on=function on(type,handler){EventEmitter.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};EventHandler.prototype.addListener=EventHandler.prototype.on;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};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};module.exports=EventHandler});define("famous/core/ElementAllocator",["require","exports","module"],function(require,exports,module){function ElementAllocator(container){if(!container)container=document.createDocumentFragment();this.container=container;this.detachedNodes={};this.nodeCount=0}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.removeChild(oldContainer.firstChild))}}this.container=container};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};ElementAllocator.prototype.deallocate=function deallocate(element){var nodeType=element.nodeName.toLowerCase();var nodeStore=this.detachedNodes[nodeType];nodeStore.push(element);this.nodeCount--};ElementAllocator.prototype.getNodeCount=function getNodeCount(){return this.nodeCount};module.exports=ElementAllocator});define("famous/utilities/Utility",["require","exports","module"],function(require,exports,module){var Utility={};Utility.Direction={X:0,Y:1,Z:2};Utility.after=function after(count,callback){var counter=count;return function(){counter--;if(counter===0)callback.apply(this,arguments)}};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()};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};module.exports=Utility});define("famous/transitions/MultipleTransition",["require","exports","module","famous/utilities/Utility"],function(require,exports,module){var Utility=require("famous/utilities/Utility");function MultipleTransition(method){this.method=method;this._instances=[];this.state=[]}MultipleTransition.SUPPORTS_MULTIPLE=true;MultipleTransition.prototype.get=function get(){for(var i=0;i<this._instances.length;i++){this.state[i]=this._instances[i].get()}return this.state};MultipleTransition.prototype.set=function set(endState,transition,callback){var _allCallback=Utility.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)}};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])}};module.exports=MultipleTransition});define("famous/transitions/TweenTransition",["require","exports","module"],function(require,exports,module){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}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<=.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};var registeredCurves={};TweenTransition.registerCurve=function registerCurve(curveName,curve){if(!registeredCurves[curveName]){registeredCurves[curveName]=curve;return true}else{return false}};TweenTransition.unregisterCurve=function unregisterCurve(curveName){if(registeredCurves[curveName]){delete registeredCurves[curveName];return true}else{return false}};TweenTransition.getCurve=function getCurve(curveName){var curve=registeredCurves[curveName];if(curve!==undefined)return curve;else throw new Error("curve not registered")};TweenTransition.getCurves=function getCurves(){return registeredCurves};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}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}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};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};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};TweenTransition.prototype.getVelocity=function getVelocity(){return this.velocity};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}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)}};TweenTransition.prototype.isActive=function isActive(){return this._active};TweenTransition.prototype.halt=function halt(){this.reset(this.get())};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}};module.exports=TweenTransition});define("famous/transitions/Transitionable",["require","exports","module","./MultipleTransition","./TweenTransition"],function(require,exports,module){var MultipleTransition=require("./MultipleTransition");var TweenTransition=require("./TweenTransition");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.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());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}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(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))}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};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=[]};Transitionable.prototype.delay=function delay(duration,callback){this.set(this.get(),{duration:duration,curve:function(){return 0}},callback)};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};Transitionable.prototype.isActive=function isActive(){return!!this.currentAction};Transitionable.prototype.halt=function halt(){this.set(this.get())};module.exports=Transitionable});define("famous/core/Context",["require","exports","module","./RenderNode","./EventHandler","./ElementAllocator","./Transform","famous/transitions/Transitionable"],function(require,exports,module){var RenderNode=require("./RenderNode");var EventHandler=require("./EventHandler");var ElementAllocator=require("./ElementAllocator");var Transform=require("./Transform");var Transitionable=require("famous/transitions/Transitionable");var _originZeroZero=[0,0];function _getElementSize(element){return[element.clientWidth,element.clientHeight]}function Context(container){this.container=container;this._allocator=new ElementAllocator(container);this._node=new RenderNode;this._eventOutput=new EventHandler;this._size=_getElementSize(this.container);this._perspectiveState=new Transitionable(0);this._perspective=undefined;this._nodeContext={allocator:this._allocator,transform:Transform.identity,opacity:1,origin:_originZeroZero,align:null,size:this._size};this._eventOutput.on("resize",function(){this.setSize(_getElementSize(this.container))}.bind(this))}Context.prototype.getAllocator=function getAllocator(){return this._allocator};Context.prototype.add=function add(obj){return this._node.add(obj)};Context.prototype.migrate=function migrate(container){if(container===this.container)return;this.container=container;this._allocator.migrate(container)};Context.prototype.getSize=function getSize(){return this._size};Context.prototype.setSize=function setSize(size){if(!size)size=_getElementSize(this.container);this._size[0]=size[0];this._size[1]=size[1]};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){this.container.style.perspective=perspective?perspective.toFixed()+"px":"";this.container.style.webkitPerspective=perspective?perspective.toFixed():"";this._perspective=perspective}this._node.commit(this._nodeContext)};Context.prototype.getPerspective=function getPerspective(){return this._perspectiveState.get()};Context.prototype.setPerspective=function setPerspective(perspective,transition,callback){return this._perspectiveState.set(perspective,transition,callback)};Context.prototype.emit=function emit(type,event){return this._eventOutput.emit(type,event)};Context.prototype.on=function on(type,handler){return this._eventOutput.on(type,handler)};Context.prototype.removeListener=function removeListener(type,handler){return this._eventOutput.removeListener(type,handler)};Context.prototype.pipe=function pipe(target){return this._eventOutput.pipe(target)};Context.prototype.unpipe=function unpipe(target){return this._eventOutput.unpipe(target)};module.exports=Context});define("famous/core/OptionsManager",["require","exports","module","./EventHandler"],function(require,exports,module){var EventHandler=require("./EventHandler");function OptionsManager(value){this._value=value;this.eventOutput=null}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;this.eventOutput.bindThis(this);EventHandler.setOutputHandler(this,this.eventOutput)}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};OptionsManager.prototype.setOptions=OptionsManager.prototype.patch;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};OptionsManager.prototype.get=function get(key){return this._value[key]};OptionsManager.prototype.getOptions=OptionsManager.prototype.get;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};OptionsManager.prototype.value=function value(){return this._value};OptionsManager.prototype.on=function on(){_createEventOutput.call(this);return this.on.apply(this,arguments)};OptionsManager.prototype.removeListener=function removeListener(){_createEventOutput.call(this);return this.removeListener.apply(this,arguments)};OptionsManager.prototype.pipe=function pipe(){_createEventOutput.call(this);return this.pipe.apply(this,arguments)};OptionsManager.prototype.unpipe=function unpipe(){_createEventOutput.call(this);return this.unpipe.apply(this,arguments)};module.exports=OptionsManager});define("famous/core/Engine",["require","exports","module","./Context","./EventHandler","./OptionsManager"],function(require,exports,module){var Context=require("./Context");var EventHandler=require("./EventHandler");var OptionsManager=require("./OptionsManager");var Engine={};var contexts=[];var nextTickQueue=[];var deferQueue=[];var lastTime=Date.now();var frameTime;var frameTimeLimit;var loopEnabled=true;var eventForwarders={};var eventHandler=new EventHandler;var options={containerType:"div",containerClass:"famous-container",fpsCap:undefined,runLoop:true};var optionsManager=new OptionsManager(options);var MAX_DEFER_FRAME_TIME=10;Engine.step=function step(){var currentTime=Date.now();if(frameTimeLimit&¤tTime-lastTime<frameTimeLimit)return;var i=0;frameTime=currentTime-lastTime;lastTime=currentTime;eventHandler.emit("prerender");for(i=0;i<nextTickQueue.length;i++)nextTickQueue[i].call(this);nextTickQueue.splice(0);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")};function loop(){if(options.runLoop){Engine.step();requestAnimationFrame(loop)}else loopEnabled=false}requestAnimationFrame(loop);function handleResize(event){for(var i=0;i<contexts.length;i++){contexts[i].emit("resize")}eventHandler.emit("resize")}window.addEventListener("resize",handleResize,false);handleResize();window.addEventListener("touchmove",function(event){event.preventDefault()},true);Engine.pipe=function pipe(target){if(target.subscribe instanceof Function)return target.subscribe(Engine);else return eventHandler.pipe(target)};Engine.unpipe=function unpipe(target){if(target.unsubscribe instanceof Function)return target.unsubscribe(Engine);else return eventHandler.unpipe(target)};Engine.on=function on(type,handler){if(!(type in eventForwarders)){eventForwarders[type]=eventHandler.emit.bind(eventHandler,type);if(document.body){document.body.addEventListener(type,eventForwarders[type])}else{Engine.nextTick(function(type,forwarder){document.body.addEventListener(type,forwarder)}.bind(this,type,eventForwarders[type]))}}return eventHandler.on(type,handler)};Engine.emit=function emit(type,event){return eventHandler.emit(type,event)};Engine.removeListener=function removeListener(type,handler){return eventHandler.removeListener(type,handler)};Engine.getFPS=function getFPS(){return 1e3/frameTime};Engine.setFPSCap=function setFPSCap(fps){frameTimeLimit=Math.floor(1e3/fps)};Engine.getOptions=function getOptions(){return optionsManager.getOptions.apply(optionsManager,arguments)};Engine.setOptions=function setOptions(options){return optionsManager.setOptions.apply(optionsManager,arguments)};Engine.createContext=function createContext(el){var needMountContainer=false;if(!el){el=document.createElement(options.containerType);el.classList.add(options.containerClass);needMountContainer=true}var context=new Context(el);Engine.registerContext(context);if(needMountContainer){Engine.nextTick(function(context,el){document.body.appendChild(el);context.emit("resize")}.bind(this,context,el))}return context};Engine.registerContext=function registerContext(context){contexts.push(context);return context};Engine.nextTick=function nextTick(fn){nextTickQueue.push(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"){if(!loopEnabled&&data.value){loopEnabled=true;requestAnimationFrame(loop)}}});module.exports=Engine});define("famous/core/Surface",["require","exports","module","./Entity","./EventHandler","./Transform"],function(require,exports,module){var Entity=require("./Entity");var EventHandler=require("./EventHandler");var Transform=require("./Transform");var devicePixelRatio=window.devicePixelRatio||1;var usePrefix=document.createElement("div").style.webkitTransform!==undefined;function Surface(options){this.options={};this.properties={};this.content="";this.classList=[];this.size=null;this._classesDirty=true;this._stylesDirty=true;this._sizeDirty=true;this._contentDirty=true;this._dirtyClasses=[];this._matrix=null;this._opacity=1;this._origin=null;this._size=null;this.eventForwarder=function eventForwarder(event){this.emit(event.type,event)}.bind(this);this.eventHandler=new EventHandler;this.eventHandler.bindThis(this);this.id=Entity.register(this);if(options)this.setOptions(options);this._currTarget=null}Surface.prototype.elementType="div";Surface.prototype.elementClass="famous-surface";Surface.prototype.on=function on(type,fn){if(this._currTarget)this._currTarget.addEventListener(type,this.eventForwarder);this.eventHandler.on(type,fn)};Surface.prototype.removeListener=function removeListener(type,fn){this.eventHandler.removeListener(type,fn)};Surface.prototype.emit=function emit(type,event){if(event&&!event.origin)event.origin=this;var handled=this.eventHandler.emit(type,event);if(handled&&event&&event.stopPropagation)event.stopPropagation();return handled};Surface.prototype.pipe=function pipe(target){return this.eventHandler.pipe(target)};Surface.prototype.unpipe=function unpipe(target){return this.eventHandler.unpipe(target)};Surface.prototype.render=function render(){return this.id};Surface.prototype.setProperties=function setProperties(properties){for(var n in properties){this.properties[n]=properties[n]}this._stylesDirty=true};Surface.prototype.getProperties=function getProperties(){return this.properties};Surface.prototype.addClass=function addClass(className){if(this.classList.indexOf(className)<0){this.classList.push(className);this._classesDirty=true}};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}};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]);for(i=0;i<classList.length;i++)this.addClass(classList[i])};Surface.prototype.getClassList=function getClassList(){return this.classList};Surface.prototype.setContent=function setContent(content){if(this.content!==content){this.content=content;this._contentDirty=true}};Surface.prototype.getContent=function getContent(){return this.content};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.content)this.setContent(options.content)};function _addEventListeners(target){for(var i in this.eventHandler.listeners){target.addEventListener(i,this.eventForwarder)}}function _removeEventListeners(target){for(var i in this.eventHandler.listeners){target.removeEventListener(i,this.eventForwarder)}}function _cleanupClasses(target){for(var i=0;i<this._dirtyClasses.length;i++)target.classList.remove(this._dirtyClasses[i]);this._dirtyClasses=[]}function _applyStyles(target){for(var n in this.properties){target.style[n]=this.properties[n]}}function _cleanupStyles(target){for(var n in this.properties){target.style[n]=""}}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]<1e-6&&m[i]>-1e-6?"0,":m[i]+","}result+=m[15]+")";return result}var _setMatrix;if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){_setMatrix=function(element,matrix){element.style.zIndex=matrix[14]*1e6|0;element.style.transform=_formatCSSTransform(matrix)}}else if(usePrefix){_setMatrix=function(element,matrix){element.style.webkitTransform=_formatCSSTransform(matrix)}}else{_setMatrix=function(element,matrix){element.style.transform=_formatCSSTransform(matrix)}}function _formatCSSOrigin(origin){return 100*origin[0]+"% "+100*origin[1]+"%"}var _setOrigin=usePrefix?function(element,origin){element.style.webkitTransformOrigin=_formatCSSOrigin(origin)}:function(element,origin){element.style.transformOrigin=_formatCSSOrigin(origin)};var _setInvisible=usePrefix?function(element){element.style.webkitTransform="scale3d(0.0001,0.0001,1)";element.style.opacity=0}:function(element){element.style.transform="scale3d(0.0001,0.0001,1)";element.style.opacity=0};function _xyNotEquals(a,b){return a&&b?a[0]!==b[0]||a[1]!==b[1]:a!==b}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="";_addEventListeners.call(this,target);this._currTarget=target;this._stylesDirty=true;this._classesDirty=true;this._sizeDirty=true;this._contentDirty=true;this._matrix=null;this._opacity=undefined;this._origin=null;this._size=null};Surface.prototype.commit=function commit(context){if(!this._currTarget)this.setup(context.allocator);var target=this._currTarget;var matrix=context.transform;var opacity=context.opacity;var origin=context.origin;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}if(this._stylesDirty){_applyStyles.call(this,target);this._stylesDirty=false}if(this._contentDirty){this.deploy(target);this.eventHandler.emit("deploy");this._contentDirty=false}if(this.size){var origSize=size;size=[this.size[0],this.size[1]];if(size[0]===undefined&&origSize[0])size[0]=origSize[0];if(size[1]===undefined&&origSize[1])size[1]=origSize[1]}if(size[0]===true)size[0]=target.clientWidth;if(size[1]===true)size[1]=target.clientHeight;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(!matrix&&this._matrix){this._matrix=null;this._opacity=0;_setInvisible(target);return}if(this._opacity!==opacity){this._opacity=opacity; | |
target.style.opacity=opacity>=1?"0.999999":opacity}if(_xyNotEquals(this._origin,origin)||Transform.notEquals(this._matrix,matrix)||this._sizeDirty){if(!matrix)matrix=Transform.identity;this._matrix=matrix;var aaMatrix=matrix;if(origin){if(!this._origin)this._origin=[0,0];this._origin[0]=origin[0];this._origin[1]=origin[1];aaMatrix=Transform.thenMove(matrix,[-this._size[0]*origin[0],-this._size[1]*origin[1],0]);_setOrigin(target,origin)}_setMatrix(target,aaMatrix)}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._sizeDirty=false}};Surface.prototype.cleanup=function cleanup(allocator){var i=0;var target=this._currTarget;this.eventHandler.emit("recall");this.recall(target);target.style.display="none";target.style.width="";target.style.height="";this._size=null;_cleanupStyles.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)}}_removeEventListeners.call(this,target);this._currTarget=null;allocator.deallocate(target);_setInvisible(target)};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};Surface.prototype.recall=function recall(target){var df=document.createDocumentFragment();while(target.hasChildNodes())df.appendChild(target.firstChild);this.setContent(df)};Surface.prototype.getSize=function getSize(actual){return actual?this._size:this.size||this._size};Surface.prototype.setSize=function setSize(size){this.size=size?[size[0],size[1]]:null;this._sizeDirty=true};module.exports=Surface});define("famous/core/Group",["require","exports","module","./Context","./Transform","./Surface"],function(require,exports,module){var Context=require("./Context");var Transform=require("./Transform");var Surface=require("./Surface");function Group(options){Surface.call(this,options);this._shouldRecalculateSize=false;this._container=document.createDocumentFragment();this.context=new Context(this._container);this.setContent(this._container);this._groupSize=[undefined,undefined]}Group.SIZE_ZERO=[0,0];Group.prototype=Object.create(Surface.prototype);Group.prototype.elementType="div";Group.prototype.elementClass="famous-group";Group.prototype.add=function add(){return this.context.add.apply(this.context,arguments)};Group.prototype.render=function render(){return Surface.prototype.render.call(this)};Group.prototype.deploy=function deploy(target){this.context.migrate(target)};Group.prototype.recall=function recall(target){this._container=document.createDocumentFragment();this.context.migrate(this._container)};Group.prototype.commit=function commit(context){var transform=context.transform;var origin=context.origin;var opacity=context.opacity;var size=context.size;var result=Surface.prototype.commit.call(this,{allocator:context.allocator,transform:Transform.thenMove(transform,[-origin[0]*size[0],-origin[1]*size[1],0]),opacity:opacity,origin:origin,size:Group.SIZE_ZERO});if(size[0]!==this._groupSize[0]||size[1]!==this._groupSize[1]){this._groupSize[0]=size[0];this._groupSize[1]=size[1];this.context.setSize(size)}this.context.update({transform:Transform.translate(-origin[0]*size[0],-origin[1]*size[1],0),origin:origin,size:size});return result};module.exports=Group});define("famous/transitions/TransitionableTransform",["require","exports","module","./Transitionable","famous/core/Transform","famous/utilities/Utility"],function(require,exports,module){var Transitionable=require("./Transitionable");var Transform=require("famous/core/Transform");var Utility=require("famous/utilities/Utility");function TransitionableTransform(transform){this._final=Transform.identity.slice();this.translate=new Transitionable([0,0,0]);this.rotate=new Transitionable([0,0,0]);this.skew=new Transitionable([0,0,0]);this.scale=new Transitionable([1,1,1]);if(transform)this.set(transform)}function _build(){return Transform.build({translate:this.translate.get(),rotate:this.rotate.get(),skew:this.skew.get(),scale:this.scale.get()})}TransitionableTransform.prototype.setTranslate=function setTranslate(translate,transition,callback){this.translate.set(translate,transition,callback);this._final=this._final.slice();this._final[12]=translate[0];this._final[13]=translate[1];if(translate[2]!==undefined)this._final[14]=translate[2];return this};TransitionableTransform.prototype.setScale=function setScale(scale,transition,callback){this.scale.set(scale,transition,callback);this._final=this._final.slice();this._final[0]=scale[0];this._final[5]=scale[1];if(scale[2]!==undefined)this._final[10]=scale[2];return this};TransitionableTransform.prototype.setRotate=function setRotate(eulerAngles,transition,callback){this.rotate.set(eulerAngles,transition,callback);this._final=_build.call(this);this._final=Transform.build({translate:this.translate.get(),rotate:eulerAngles,scale:this.scale.get(),skew:this.skew.get()});return this};TransitionableTransform.prototype.setSkew=function setSkew(skewAngles,transition,callback){this.skew.set(skewAngles,transition,callback);this._final=Transform.build({translate:this.translate.get(),rotate:this.rotate.get(),scale:this.scale.get(),skew:skewAngles});return this};TransitionableTransform.prototype.set=function set(transform,transition,callback){this._final=transform;var components=Transform.interpret(transform);var _callback=callback?Utility.after(4,callback):null;this.translate.set(components.translate,transition,_callback);this.rotate.set(components.rotate,transition,_callback);this.skew.set(components.skew,transition,_callback);this.scale.set(components.scale,transition,_callback);return this};TransitionableTransform.prototype.setDefaultTransition=function setDefaultTransition(transition){this.translate.setDefault(transition);this.rotate.setDefault(transition);this.skew.setDefault(transition);this.scale.setDefault(transition)};TransitionableTransform.prototype.get=function get(){if(this.isActive()){return _build.call(this)}else return this._final};TransitionableTransform.prototype.getFinal=function getFinal(){return this._final};TransitionableTransform.prototype.isActive=function isActive(){return this.translate.isActive()||this.rotate.isActive()||this.scale.isActive()||this.skew.isActive()};TransitionableTransform.prototype.halt=function halt(){this._final=this.get();this.translate.halt();this.rotate.halt();this.skew.halt();this.scale.halt()};module.exports=TransitionableTransform});define("famous/core/Modifier",["require","exports","module","./Transform","famous/transitions/Transitionable","famous/transitions/TransitionableTransform"],function(require,exports,module){var Transform=require("./Transform");var Transitionable=require("famous/transitions/Transitionable");var TransitionableTransform=require("famous/transitions/TransitionableTransform");function Modifier(options){this._transformGetter=null;this._opacityGetter=null;this._originGetter=null;this._alignGetter=null;this._sizeGetter=null;this._legacyStates={};this._output={transform:Transform.identity,opacity:1,origin:null,align:null,size: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)}}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};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};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};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};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};Modifier.prototype.setTransform=function setTransform(transform,transition,callback){if(transition||this._legacyStates.transform){if(!this._legacyStates.transform){this._legacyStates.transform=new TransitionableTransform(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)};Modifier.prototype.setOpacity=function setOpacity(opacity,transition,callback){if(transition||this._legacyStates.opacity){if(!this._legacyStates.opacity){this._legacyStates.opacity=new Transitionable(this._output.opacity)}if(!this._opacityGetter)this.opacityFrom(this._legacyStates.opacity);return this._legacyStates.opacity.set(opacity,transition,callback)}else return this.opacityFrom(opacity)};Modifier.prototype.setOrigin=function setOrigin(origin,transition,callback){if(transition||this._legacyStates.origin){if(!this._legacyStates.origin){this._legacyStates.origin=new Transitionable(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)};Modifier.prototype.setAlign=function setAlign(align,transition,callback){if(transition||this._legacyStates.align){if(!this._legacyStates.align){this._legacyStates.align=new Transitionable(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)};Modifier.prototype.setSize=function setSize(size,transition,callback){if(size&&(transition||this._legacyStates.size)){if(!this._legacyStates.size){this._legacyStates.size=new Transitionable(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)};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();this._transformGetter=null;this._opacityGetter=null;this._originGetter=null;this._alignGetter=null;this._sizeGetter=null};Modifier.prototype.getTransform=function getTransform(){return this._transformGetter()};Modifier.prototype.getFinalTransform=function getFinalTransform(){return this._legacyStates.transform?this._legacyStates.transform.getFinal():this._output.transform};Modifier.prototype.getOpacity=function getOpacity(){return this._opacityGetter()};Modifier.prototype.getOrigin=function getOrigin(){return this._originGetter()};Modifier.prototype.getAlign=function getAlign(){return this._alignGetter()};Modifier.prototype.getSize=function getSize(){return this._sizeGetter?this._sizeGetter():this._output.size};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()}Modifier.prototype.modify=function modify(target){_update.call(this);this._output.target=target;return this._output};module.exports=Modifier});define("famous/core/Scene",["require","exports","module","./Transform","./Modifier","./RenderNode"],function(require,exports,module){var Transform=require("./Transform");var Modifier=require("./Modifier");var RenderNode=require("./RenderNode");function Scene(definition){this.id=null;this._objects=null;this.node=new RenderNode;this._definition=null;if(definition)this.load(definition)}var _MATRIX_GENERATORS={translate:Transform.translate,rotate:Transform.rotate,rotateX:Transform.rotateX,rotateY:Transform.rotateY,rotateZ:Transform.rotateZ,rotateAxis:Transform.rotateAxis,scale:Transform.scale,skew:Transform.skew,matrix3d:function(){return arguments}};Scene.prototype.create=function create(){return new Scene(this._definition)};function _resolveTransformMatrix(matrixDefinition){for(var type in _MATRIX_GENERATORS){if(type in matrixDefinition){var args=matrixDefinition[type];if(!(args instanceof Array))args=[args];return _MATRIX_GENERATORS[type].apply(this,args)}}}function _parseTransform(definition){var transformDefinition=definition.transform;var opacity=definition.opacity;var origin=definition.origin;var size=definition.size;var transform=Transform.identity;if(transformDefinition instanceof Array){if(transformDefinition.length===16&&typeof transformDefinition[0]==="number"){transform=transformDefinition}else{for(var i=0;i<transformDefinition.length;i++){transform=Transform.multiply(transform,_resolveTransformMatrix(transformDefinition[i]))}}}else if(transformDefinition instanceof Object){transform=_resolveTransformMatrix(transformDefinition)}var result=new Modifier({transform:transform,opacity:opacity,origin:origin,size:size});return result}function _parseArray(definition){var result=new RenderNode;for(var i=0;i<definition.length;i++){var obj=_parse.call(this,definition[i]);if(obj)result.add(obj)}return result}function _parse(definition){var result;var id;if(definition instanceof Array){result=_parseArray.call(this,definition)}else{id=this._objects.length;if(definition.render&&definition.render instanceof Function){result=definition}else if(definition.target){var targetObj=_parse.call(this,definition.target);var obj=_parseTransform.call(this,definition);result=new RenderNode(obj);result.add(targetObj);if(definition.id)this.id[definition.id]=obj}else if(definition.id){result=new RenderNode;this.id[definition.id]=result}}this._objects[id]=result;return result}Scene.prototype.load=function load(definition){this._definition=definition;this.id={};this._objects=[];this.node.set(_parse.call(this,definition))};Scene.prototype.add=function add(){return this.node.add.apply(this.node,arguments)};Scene.prototype.render=function render(){return this.node.render.apply(this.node,arguments)};module.exports=Scene});define("famous/core/View",["require","exports","module","./EventHandler","./OptionsManager","./RenderNode"],function(require,exports,module){var EventHandler=require("./EventHandler");var OptionsManager=require("./OptionsManager");var RenderNode=require("./RenderNode");function View(options){this._node=new RenderNode;this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this.options=Object.create(this.constructor.DEFAULT_OPTIONS||View.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options)}View.DEFAULT_OPTIONS={};View.prototype.getOptions=function getOptions(){return this._optionsManager.value()};View.prototype.setOptions=function setOptions(options){this._optionsManager.patch(options)};View.prototype.add=function add(){return this._node.add.apply(this._node,arguments)};View.prototype._add=View.prototype.add;View.prototype.render=function render(){return this._node.render()};View.prototype.getSize=function getSize(){if(this._node&&this._node.getSize){return this._node.getSize.apply(this._node,arguments)||this.options.size}else return this.options.size};module.exports=View});define("famous/core/ViewSequence",["require","exports","module"],function(require,exports,module){function ViewSequence(options){if(!options)options=[];if(options instanceof Array)options={array:options};this._=null;this.index=options.index||0;if(options.array)this._=new this.constructor.Backing(options.array);else if(options._)this._=options._;if(this.index===this._.firstIndex)this._.firstNode=this;if(this.index===this._.firstIndex+this._.array.length-1)this._.lastNode=this;if(options.loop!==undefined)this._.loop=options.loop;this._previousNode=null;this._nextNode=null}ViewSequence.Backing=function Backing(array){this.array=array;this.firstIndex=0;this.loop=false;this.firstNode=null;this.lastNode=null};ViewSequence.Backing.prototype.getValue=function getValue(i){var _i=i-this.firstIndex;if(_i<0||_i>=this.array.length)return null;return this.array[_i]};ViewSequence.Backing.prototype.setValue=function setValue(i,value){this.array[i-this.firstIndex]=value};ViewSequence.Backing.prototype.reindex=function reindex(start,removeCount,insertCount){if(!this.array[0])return;var i=0;var index=this.firstIndex;var indexShiftAmount=insertCount-removeCount;var node=this.firstNode;while(index<start-1){node=node.getNext();index++}var spliceStartNode=node;for(i=0;i<removeCount;i++){node=node.getNext();if(node)node._previousNode=spliceStartNode}var spliceResumeNode=node?node.getNext():null;spliceStartNode._nextNode=null;node=spliceStartNode;for(i=0;i<insertCount;i++)node=node.getNext();index+=insertCount;if(node!==spliceResumeNode){node._nextNode=spliceResumeNode;if(spliceResumeNode)spliceResumeNode._previousNode=node}if(spliceResumeNode){node=spliceResumeNode;index++;while(node&&index<this.array.length+this.firstIndex){if(node._nextNode)node.index+=indexShiftAmount;else node.index=index;node=node.getNext();index++}}};ViewSequence.prototype.getPrevious=function getPrevious(){if(this.index===this._.firstIndex){if(this._.loop){this._previousNode=this._.lastNode||new this.constructor({_:this._,index:this._.firstIndex+this._.array.length-1});this._previousNode._nextNode=this}else{this._previousNode=null}}else if(!this._previousNode){this._previousNode=new this.constructor({_:this._,index:this.index-1});this._previousNode._nextNode=this}return this._previousNode};ViewSequence.prototype.getNext=function getNext(){if(this.index===this._.firstIndex+this._.array.length-1){if(this._.loop){this._nextNode=this._.firstNode||new this.constructor({_:this._,index:this._.firstIndex});this._nextNode._previousNode=this}else{this._nextNode=null}}else if(!this._nextNode){this._nextNode=new this.constructor({_:this._,index:this.index+1});this._nextNode._previousNode=this}return this._nextNode};ViewSequence.prototype.getIndex=function getIndex(){return this.index};ViewSequence.prototype.toString=function toString(){return""+this.index};ViewSequence.prototype.unshift=function unshift(value){this._.array.unshift.apply(this._.array,arguments);this._.firstIndex-=arguments.length};ViewSequence.prototype.push=function push(value){this._.array.push.apply(this._.array,arguments)};ViewSequence.prototype.splice=function splice(index,howMany){var values=Array.prototype.slice.call(arguments,2);this._.array.splice.apply(this._.array,[index-this._.firstIndex,howMany].concat(values));this._.reindex(index,howMany,values.length)};ViewSequence.prototype.swap=function swap(other){var otherValue=other.get();var myValue=this.get();this._.setValue(this.index,otherValue);this._.setValue(other.index,myValue);var myPrevious=this._previousNode;var myNext=this._nextNode;var myIndex=this.index;var otherPrevious=other._previousNode;var otherNext=other._nextNode;var otherIndex=other.index;this.index=otherIndex;this._previousNode=otherPrevious===this?other:otherPrevious;if(this._previousNode)this._previousNode._nextNode=this;this._nextNode=otherNext===this?other:otherNext;if(this._nextNode)this._nextNode._previousNode=this;other.index=myIndex;other._previousNode=myPrevious===other?this:myPrevious;if(other._previousNode)other._previousNode._nextNode=other;other._nextNode=myNext===other?this:myNext;if(other._nextNode)other._nextNode._previousNode=other;if(this.index===this._.firstIndex)this._.firstNode=this;else if(this.index===this._.firstIndex+this._.array.length-1)this._.lastNode=this;if(other.index===this._.firstIndex)this._.firstNode=other;else if(other.index===this._.firstIndex+this._.array.length-1)this._.lastNode=other};ViewSequence.prototype.get=function get(){return this._.getValue(this.index)};ViewSequence.prototype.getSize=function getSize(){var target=this.get();return target?target.getSize():null};ViewSequence.prototype.render=function render(){var target=this.get();return target?target.render.apply(target,arguments):null};module.exports=ViewSequence});define("famous/math/Utilities",["require","exports","module"],function(require,exports,module){var Utilities={};Utilities.clamp=function clamp(value,range){return Math.max(Math.min(value,range[1]),range[0])};Utilities.length=function length(array){var distanceSquared=0;for(var i=0;i<array.length;i++){distanceSquared+=array[i]*array[i]}return Math.sqrt(distanceSquared)};module.exports=Utilities});define("famous/inputs/GenericSync",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function GenericSync(syncs,options){this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.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;var registry={};GenericSync.register=function register(syncObject){for(var key in syncObject){if(registry[key]){if(registry[key]===syncObject[key])return;else throw new Error("this key is registered to a different sync class")}else registry[key]=syncObject[key]}};GenericSync.prototype.setOptions=function(options){for(var key in this._syncs){this._syncs[key].setOptions(options)}};GenericSync.prototype.pipeSync=function pipeToSync(key){var sync=this._syncs[key];this._eventInput.pipe(sync);sync.pipe(this._eventOutput)};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)}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])};module.exports=GenericSync});define("famous/inputs/MouseSync",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function MouseSync(options){this.options=Object.create(MouseSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this._eventInput.on("mousedown",_handleStart.bind(this));this._eventInput.on("mousemove",_handleMove.bind(this));this._eventInput.on("mouseup",_handleEnd.bind(this));if(this.options.propogate)this._eventInput.on("mouseleave",_handleLeave.bind(this));else this._eventInput.on("mouseleave",_handleEnd.bind(this));this._payload={delta:null,position:null,velocity:null,clientX:0,clientY:0,offsetX:0,offsetY:0};this._position=null;this._prevCoord=undefined;this._prevTime=undefined;this._down=false;this._moved=false}MouseSync.DEFAULT_OPTIONS={direction:undefined,rails:false,scale:1,propogate:true};MouseSync.DIRECTION_X=0;MouseSync.DIRECTION_Y=1;var MINIMUM_TICK_TIME=8;var _now=Date.now;function _handleStart(event){var delta;var velocity;event.preventDefault();var x=event.clientX;var y=event.clientY;this._prevCoord=[x,y];this._prevTime=_now();this._down=true;this._move=false;if(this.options.direction!==undefined){this._position=0;delta=0;velocity=0}else{this._position=[0,0];delta=[0,0];velocity=[0,0]}var payload=this._payload;payload.delta=delta;payload.position=this._position;payload.velocity=velocity;payload.clientX=x;payload.clientY=y;payload.offsetX=event.offsetX;payload.offsetY=event.offsetY;this._eventOutput.emit("start",payload)}function _handleMove(event){if(!this._prevCoord)return;var prevCoord=this._prevCoord;var prevTime=this._prevTime;var x=event.clientX;var y=event.clientY;var currTime=_now();var diffX=x-prevCoord[0];var diffY=y-prevCoord[1];if(this.options.rails){if(Math.abs(diffX)>Math.abs(diffY))diffY=0;else diffX=0}var diffTime=Math.max(currTime-prevTime,MINIMUM_TICK_TIME);var velX=diffX/diffTime;var velY=diffY/diffTime;var scale=this.options.scale;var nextVel;var nextDelta;if(this.options.direction===MouseSync.DIRECTION_X){nextDelta=scale*diffX;nextVel=scale*velX;this._position+=nextDelta}else if(this.options.direction===MouseSync.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.position=this._position;payload.velocity=nextVel;payload.clientX=x;payload.clientY=y;payload.offsetX=event.offsetX;payload.offsetY=event.offsetY;this._eventOutput.emit("update",payload);this._prevCoord=[x,y];this._prevTime=currTime;this._move=true}function _handleEnd(event){if(!this._down)return;this._eventOutput.emit("end",this._payload);this._prevCoord=undefined;this._prevTime=undefined;this._down=false;this._move=false}function _handleLeave(event){if(!this._down||!this._move)return;var boundMove=_handleMove.bind(this);var boundEnd=function(event){_handleEnd.call(this,event);document.removeEventListener("mousemove",boundMove);document.removeEventListener("mouseup",boundEnd)}.bind(this,event);document.addEventListener("mousemove",boundMove);document.addEventListener("mouseup",boundEnd)}MouseSync.prototype.getOptions=function getOptions(){return this.options};MouseSync.prototype.setOptions=function setOptions(options){if(options.direction!==undefined)this.options.direction=options.direction;if(options.rails!==undefined)this.options.rails=options.rails;if(options.scale!==undefined)this.options.scale=options.scale;if(options.propogate!==undefined)this.options.propogate=options.propogate};module.exports=MouseSync});define("famous/inputs/TouchTracker",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");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(event){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(event){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(event){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]}}}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]}}function TouchTracker(selective){this.selective=selective;this.touchHistory={};this.eventInput=new EventHandler;this.eventOutput=new EventHandler;EventHandler.setInputHandler(this,this.eventInput);EventHandler.setOutputHandler(this,this.eventOutput);this.eventInput.on("touchstart",_handleStart.bind(this));this.eventInput.on("touchmove",_handleMove.bind(this));this.eventInput.on("touchend",_handleEnd.bind(this));this.eventInput.on("touchcancel",_handleEnd.bind(this));this.eventInput.on("unpipe",_handleUnpipe.bind(this))}TouchTracker.prototype.track=function track(data){this.touchHistory[data.identifier]=[data]};module.exports=TouchTracker});define("famous/inputs/TouchSync",["require","exports","module","./TouchTracker","famous/core/EventHandler"],function(require,exports,module){var TouchTracker=require("./TouchTracker");var EventHandler=require("famous/core/EventHandler");function TouchSync(options){this.options=Object.create(TouchSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._eventOutput=new EventHandler;this._touchTracker=new TouchTracker;EventHandler.setOutputHandler(this,this._eventOutput);EventHandler.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}TouchSync.DEFAULT_OPTIONS={direction:undefined,rails:false,scale:1};TouchSync.DIRECTION_X=0;TouchSync.DIRECTION_Y=1;var MINIMUM_TICK_TIME=8;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)}function _handleMove(data){var history=data.history;var currHistory=history[history.length-1];var prevHistory=history[history.length-2];var prevTime=prevHistory.timestamp;var currTime=currHistory.timestamp;var diffX=currHistory.x-prevHistory.x;var diffY=currHistory.y-prevHistory.y;if(this.options.rails){if(Math.abs(diffX)>Math.abs(diffY))diffY=0;else diffX=0}var diffTime=Math.max(currTime-prevTime,MINIMUM_TICK_TIME);var velX=diffX/diffTime;var velY=diffY/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)}function _handleEnd(data){this._payload.count=data.count;this._eventOutput.emit("end",this._payload)}TouchSync.prototype.setOptions=function setOptions(options){if(options.direction!==undefined)this.options.direction=options.direction;if(options.rails!==undefined)this.options.rails=options.rails;if(options.scale!==undefined)this.options.scale=options.scale};TouchSync.prototype.getOptions=function getOptions(){return this.options};module.exports=TouchSync});define("famous/modifiers/Draggable",["require","exports","module","famous/core/Transform","famous/transitions/Transitionable","famous/core/EventHandler","famous/math/Utilities","famous/inputs/GenericSync","famous/inputs/MouseSync","famous/inputs/TouchSync"],function(require,exports,module){var Transform=require("famous/core/Transform");var Transitionable=require("famous/transitions/Transitionable");var EventHandler=require("famous/core/EventHandler");var Utilities=require("famous/math/Utilities");var GenericSync=require("famous/inputs/GenericSync");var MouseSync=require("famous/inputs/MouseSync");var TouchSync=require("famous/inputs/TouchSync");GenericSync.register({mouse:MouseSync,touch:TouchSync});function Draggable(options){this.options=Object.create(Draggable.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._positionState=new Transitionable([0,0]);this._differential=[0,0];this._active=true;this.sync=new GenericSync(["mouse","touch"],{scale:this.options.scale});this.eventOutput=new EventHandler;EventHandler.setInputHandler(this,this.sync);EventHandler.setOutputHandler(this,this.eventOutput);_bindEvents.call(this)}var _direction={x:1,y:2};Draggable.DIRECTION_X=_direction.x;Draggable.DIRECTION_Y=_direction.y;var _clamp=Utilities.clamp;Draggable.DEFAULT_OPTIONS={projection:_direction.x|_direction.y,scale:1,xRange:null,yRange:null,snapX:0,snapY:0,transition:{duration:0}};function _mapDifferential(differential){var opts=this.options;var projection=opts.projection;var snapX=opts.snapX;var snapY=opts.snapY;var tx=projection&_direction.x?differential[0]:0;var ty=projection&_direction.y?differential[1]:0;if(snapX>0)tx-=tx%snapX;if(snapY>0)ty-=ty%snapY;return[tx,ty]}function _handleStart(){if(!this._active)return;if(this._positionState.isActive())this._positionState.halt();this.eventOutput.emit("start",{position:this.getPosition()})}function _handleMove(event){if(!this._active)return;var options=this.options;this._differential=event.position;var newDifferential=_mapDifferential.call(this,this._differential);this._differential[0]-=newDifferential[0];this._differential[1]-=newDifferential[1];var pos=this.getPosition();pos[0]+=newDifferential[0];pos[1]+=newDifferential[1];if(options.xRange){var xRange=[options.xRange[0]+.5*options.snapX,options.xRange[1]-.5*options.snapX];pos[0]=_clamp(pos[0],xRange)}if(options.yRange){var yRange=[options.yRange[0]+.5*options.snapY,options.yRange[1]-.5*options.snapY];pos[1]=_clamp(pos[1],yRange)}this.eventOutput.emit("update",{position:pos})}function _handleEnd(){if(!this._active)return;this.eventOutput.emit("end",{position:this.getPosition()})}function _bindEvents(){this.sync.on("start",_handleStart.bind(this));this.sync.on("update",_handleMove.bind(this));this.sync.on("end",_handleEnd.bind(this))}Draggable.prototype.setOptions=function setOptions(options){var currentOptions=this.options;if(options.projection!==undefined){var proj=options.projection;this.options.projection=0;["x","y"].forEach(function(val){if(proj.indexOf(val)!==-1)currentOptions.projection|=_direction[val]})}if(options.scale!==undefined){currentOptions.scale=options.scale;this.sync.setOptions({scale:options.scale})}if(options.xRange!==undefined)currentOptions.xRange=options.xRange;if(options.yRange!==undefined)currentOptions.yRange=options.yRange;if(options.snapX!==undefined)currentOptions.snapX=options.snapX;if(options.snapY!==undefined)currentOptions.snapY=options.snapY};Draggable.prototype.getPosition=function getPosition(){return this._positionState.get()};Draggable.prototype.setRelativePosition=function setRelativePosition(position,transition,callback){var currPos=this.getPosition();var relativePosition=[currPos[0]+position[0],currPos[1]+position[1]];this.setPosition(relativePosition,transition,callback)};Draggable.prototype.setPosition=function setPosition(position,transition,callback){if(this._positionState.isActive())this._positionState.halt();this._positionState.set(position,transition,callback)};Draggable.prototype.activate=function activate(){this._active=true};Draggable.prototype.deactivate=function deactivate(){this._active=false};Draggable.prototype.toggle=function toggle(){this._active=!this._active};Draggable.prototype.modify=function modify(target){var pos=this.getPosition();return{transform:Transform.translate(pos[0],pos[1]),target:target}};module.exports=Draggable});define("famous/modifiers/Fader",["require","exports","module","famous/transitions/Transitionable","famous/core/OptionsManager"],function(require,exports,module){var Transitionable=require("famous/transitions/Transitionable");var OptionsManager=require("famous/core/OptionsManager");function Fader(options,startState){this.options=Object.create(Fader.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);if(!startState)startState=0;this.transitionHelper=new Transitionable(startState)}Fader.DEFAULT_OPTIONS={cull:false,transition:true,pulseInTransition:true,pulseOutTransition:true};Fader.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};Fader.prototype.show=function show(transition,callback){transition=transition||this.options.transition;this.set(1,transition,callback)};Fader.prototype.hide=function hide(transition,callback){transition=transition||this.options.transition;this.set(0,transition,callback)};Fader.prototype.set=function set(state,transition,callback){this.halt();this.transitionHelper.set(state,transition,callback)};Fader.prototype.halt=function halt(){this.transitionHelper.halt()};Fader.prototype.isVisible=function isVisible(){return this.transitionHelper.get()>0};Fader.prototype.modify=function modify(target){var currOpacity=this.transitionHelper.get();if(this.options.cull&&!currOpacity)return undefined;else return{opacity:currOpacity,target:target}};module.exports=Fader});define("famous/modifiers/ModifierChain",["require","exports","module"],function(require,exports,module){function ModifierChain(){this._chain=[];if(arguments.length)this.addModifier.apply(this,arguments)}ModifierChain.prototype.addModifier=function addModifier(varargs){Array.prototype.push.apply(this._chain,arguments)};ModifierChain.prototype.removeModifier=function removeModifier(modifier){var index=this._chain.indexOf(modifier);if(index<0)return;this._chain.splice(index,1)};ModifierChain.prototype.modify=function modify(input){var chain=this._chain;var result=input;for(var i=0;i<chain.length;i++){result=chain[i].modify(result)}return result};module.exports=ModifierChain});define("famous/modifiers/StateModifier",["require","exports","module","famous/core/Modifier","famous/core/Transform","famous/transitions/Transitionable","famous/transitions/TransitionableTransform"],function(require,exports,module){var Modifier=require("famous/core/Modifier");var Transform=require("famous/core/Transform");var Transitionable=require("famous/transitions/Transitionable");var TransitionableTransform=require("famous/transitions/TransitionableTransform");function StateModifier(options){this._transformState=new TransitionableTransform(Transform.identity);this._opacityState=new Transitionable(1);this._originState=new Transitionable([0,0]);this._alignState=new Transitionable([0,0]);this._sizeState=new Transitionable([0,0]);this._modifier=new Modifier({transform:this._transformState,opacity:this._opacityState,origin:null,align:null,size:null});this._hasOrigin=false;this._hasAlign=false;this._hasSize=false;if(options){if(options.transform)this.setTransform(options.transform);if(options.opacity!==undefined)this.setOpacity(options.opacity);if(options.origin)this.setOrigin(options.origin);if(options.align)this.setAlign(options.align);if(options.size)this.setSize(options.size)}}StateModifier.prototype.setTransform=function setTransform(transform,transition,callback){this._transformState.set(transform,transition,callback);return this};StateModifier.prototype.setOpacity=function setOpacity(opacity,transition,callback){this._opacityState.set(opacity,transition,callback);return this};StateModifier.prototype.setOrigin=function setOrigin(origin,transition,callback){if(origin===null){if(this._hasOrigin){this._modifier.originFrom(null);this._hasOrigin=false}return this}else if(!this._hasOrigin){this._hasOrigin=true;this._modifier.originFrom(this._originState)}this._originState.set(origin,transition,callback);return this};StateModifier.prototype.setAlign=function setOrigin(align,transition,callback){if(align===null){if(this._hasAlign){this._modifier.alignFrom(null);this._hasAlign=false}return this}else if(!this._hasAlign){this._hasAlign=true;this._modifier.alignFrom(this._alignState)}this._alignState.set(align,transition,callback);return this};StateModifier.prototype.setSize=function setSize(size,transition,callback){if(size===null){if(this._hasSize){this._modifier.sizeFrom(null);this._hasSize=false}return this}else if(!this._hasSize){this._hasSize=true;this._modifier.sizeFrom(this._sizeState)}this._sizeState.set(size,transition,callback);return this};StateModifier.prototype.halt=function halt(){this._transformState.halt();this._opacityState.halt();this._originState.halt();this._alignState.halt();this._sizeState.halt()};StateModifier.prototype.getTransform=function getTransform(){return this._transformState.get()};StateModifier.prototype.getFinalTransform=function getFinalTransform(){return this._transformState.getFinal()};StateModifier.prototype.getOpacity=function getOpacity(){return this._opacityState.get()};StateModifier.prototype.getOrigin=function getOrigin(){return this._hasOrigin?this._originState.get():null};StateModifier.prototype.getAlign=function getAlign(){return this._hasAlign?this._alignState.get():null};StateModifier.prototype.getSize=function getSize(){return this._hasSize?this._sizeState.get():null};StateModifier.prototype.modify=function modify(target){return this._modifier.modify(target)};module.exports=StateModifier});define("famous/math/Vector",["require","exports","module"],function(require,exports,module){function Vector(x,y,z){if(arguments.length===1)this.set(x);else{this.x=x||0;this.y=y||0;this.z=z||0}return this}var _register=new Vector(0,0,0);Vector.prototype.add=function add(v){return _setXYZ.call(_register,this.x+v.x,this.y+v.y,this.z+v.z)};Vector.prototype.sub=function sub(v){return _setXYZ.call(_register,this.x-v.x,this.y-v.y,this.z-v.z)};Vector.prototype.mult=function mult(r){return _setXYZ.call(_register,r*this.x,r*this.y,r*this.z)};Vector.prototype.div=function div(r){return this.mult(1/r)};Vector.prototype.cross=function cross(v){var x=this.x;var y=this.y;var z=this.z;var vx=v.x;var vy=v.y;var vz=v.z;return _setXYZ.call(_register,z*vy-y*vz,x*vz-z*vx,y*vx-x*vy)};Vector.prototype.equals=function equals(v){return v.x===this.x&&v.y===this.y&&v.z===this.z};Vector.prototype.rotateX=function rotateX(theta){var x=this.x;var y=this.y;var z=this.z;var cosTheta=Math.cos(theta);var sinTheta=Math.sin(theta);return _setXYZ.call(_register,x,y*cosTheta-z*sinTheta,y*sinTheta+z*cosTheta)};Vector.prototype.rotateY=function rotateY(theta){var x=this.x;var y=this.y;var z=this.z;var cosTheta=Math.cos(theta);var sinTheta=Math.sin(theta);return _setXYZ.call(_register,z*sinTheta+x*cosTheta,y,z*cosTheta-x*sinTheta)};Vector.prototype.rotateZ=function rotateZ(theta){var x=this.x;var y=this.y;var z=this.z;var cosTheta=Math.cos(theta);var sinTheta=Math.sin(theta);return _setXYZ.call(_register,x*cosTheta-y*sinTheta,x*sinTheta+y*cosTheta,z)};Vector.prototype.dot=function dot(v){return this.x*v.x+this.y*v.y+this.z*v.z};Vector.prototype.normSquared=function normSquared(){return this.dot(this)};Vector.prototype.norm=function norm(){return Math.sqrt(this.normSquared())};Vector.prototype.normalize=function normalize(length){if(arguments.length===0)length=1;var norm=this.norm();if(norm>1e-7)return _setFromVector.call(_register,this.mult(length/norm));else return _setXYZ.call(_register,length,0,0)};Vector.prototype.clone=function clone(){return new Vector(this)};Vector.prototype.isZero=function isZero(){return!(this.x||this.y||this.z)};function _setXYZ(x,y,z){this.x=x;this.y=y;this.z=z;return this}function _setFromArray(v){return _setXYZ.call(this,v[0],v[1],v[2]||0)}function _setFromVector(v){return _setXYZ.call(this,v.x,v.y,v.z)}function _setFromNumber(x){return _setXYZ.call(this,x,0,0)}Vector.prototype.set=function set(v){if(v instanceof Array)return _setFromArray.call(this,v);if(v instanceof Vector)return _setFromVector.call(this,v);if(typeof v==="number")return _setFromNumber.call(this,v)};Vector.prototype.setXYZ=function(x,y,z){return _setXYZ.apply(this,arguments)};Vector.prototype.set1D=function(x){return _setFromNumber.call(this,x)};Vector.prototype.put=function put(v){_setFromVector.call(v,_register)};Vector.prototype.clear=function clear(){return _setXYZ.call(this,0,0,0)};Vector.prototype.cap=function cap(cap){if(cap===Infinity)return _setFromVector.call(_register,this);var norm=this.norm();if(norm>cap)return _setFromVector.call(_register,this.mult(cap/norm));else return _setFromVector.call(_register,this)};Vector.prototype.project=function project(n){return n.mult(this.dot(n))};Vector.prototype.reflectAcross=function reflectAcross(n){n.normalize().put(n);return _setFromVector(_register,this.sub(this.project(n).mult(2)))};Vector.prototype.get=function get(){return[this.x,this.y,this.z]};Vector.prototype.get1D=function(){return this.x};module.exports=Vector});define("famous/math/Matrix",["require","exports","module","./Vector"],function(require,exports,module){var Vector=require("./Vector");function Matrix(values){this.values=values||[[1,0,0],[0,1,0],[0,0,1]];return this}var _register=new Matrix;var _vectorRegister=new Vector;Matrix.prototype.get=function get(){return this.values};Matrix.prototype.set=function set(values){this.values=values};Matrix.prototype.vectorMultiply=function vectorMultiply(v){var M=this.get();var v0=v.x;var v1=v.y;var v2=v.z;var M0=M[0];var M1=M[1];var M2=M[2];var M00=M0[0];var M01=M0[1];var M02=M0[2];var M10=M1[0];var M11=M1[1];var M12=M1[2];var M20=M2[0];var M21=M2[1];var M22=M2[2];return _vectorRegister.setXYZ(M00*v0+M01*v1+M02*v2,M10*v0+M11*v1+M12*v2,M20*v0+M21*v1+M22*v2)};Matrix.prototype.multiply=function multiply(M2){var M1=this.get();var result=[[]];for(var i=0;i<3;i++){result[i]=[];for(var j=0;j<3;j++){var sum=0;for(var k=0;k<3;k++){sum+=M1[i][k]*M2[k][j]}result[i][j]=sum}}return _register.set(result)};Matrix.prototype.transpose=function transpose(){var result=[];var M=this.get();for(var row=0;row<3;row++){for(var col=0;col<3;col++){result[row][col]=M[col][row]}}return _register.set(result)};Matrix.prototype.clone=function clone(){var values=this.get();var M=[];for(var row=0;row<3;row++)M[row]=values[row].slice();return new Matrix(M)};module.exports=Matrix});define("famous/math/Quaternion",["require","exports","module","./Matrix"],function(require,exports,module){var Matrix=require("./Matrix");function Quaternion(w,x,y,z){if(arguments.length===1)this.set(w);else{this.w=w!==undefined?w:1;this.x=x!==undefined?x:0;this.y=y!==undefined?y:0;this.z=z!==undefined?z:0}return this}var register=new Quaternion(1,0,0,0);Quaternion.prototype.add=function add(q){return register.setWXYZ(this.w+q.w,this.x+q.x,this.y+q.y,this.z+q.z)};Quaternion.prototype.sub=function sub(q){return register.setWXYZ(this.w-q.w,this.x-q.x,this.y-q.y,this.z-q.z)};Quaternion.prototype.scalarDivide=function scalarDivide(s){return this.scalarMultiply(1/s)};Quaternion.prototype.scalarMultiply=function scalarMultiply(s){return register.setWXYZ(this.w*s,this.x*s,this.y*s,this.z*s)};Quaternion.prototype.multiply=function multiply(q){var x1=this.x;var y1=this.y;var z1=this.z;var w1=this.w;var x2=q.x;var y2=q.y;var z2=q.z;var w2=q.w||0;return register.setWXYZ(w1*w2-x1*x2-y1*y2-z1*z2,x1*w2+x2*w1+y2*z1-y1*z2,y1*w2+y2*w1+x1*z2-x2*z1,z1*w2+z2*w1+x2*y1-x1*y2)};var conj=new Quaternion(1,0,0,0);Quaternion.prototype.rotateVector=function rotateVector(v){conj.set(this.conj());return register.set(this.multiply(v).multiply(conj))};Quaternion.prototype.inverse=function inverse(){return register.set(this.conj().scalarDivide(this.normSquared()))};Quaternion.prototype.negate=function negate(){return this.scalarMultiply(-1)};Quaternion.prototype.conj=function conj(){return register.setWXYZ(this.w,-this.x,-this.y,-this.z)};Quaternion.prototype.normalize=function normalize(length){length=length===undefined?1:length;return this.scalarDivide(length*this.norm())};Quaternion.prototype.makeFromAngleAndAxis=function makeFromAngleAndAxis(angle,v){var n=v.normalize();var ha=angle*.5;var s=-Math.sin(ha);this.x=s*n.x;this.y=s*n.y;this.z=s*n.z;this.w=Math.cos(ha);return this};Quaternion.prototype.setWXYZ=function setWXYZ(w,x,y,z){register.clear();this.w=w;this.x=x;this.y=y;this.z=z;return this};Quaternion.prototype.set=function set(v){if(v instanceof Array){this.w=v[0];this.x=v[1];this.y=v[2];this.z=v[3]}else{this.w=v.w;this.x=v.x;this.y=v.y;this.z=v.z}if(this!==register)register.clear();return this};Quaternion.prototype.put=function put(q){q.set(register)};Quaternion.prototype.clone=function clone(){return new Quaternion(this)};Quaternion.prototype.clear=function clear(){this.w=1;this.x=0;this.y=0;this.z=0;return this};Quaternion.prototype.isEqual=function isEqual(q){return q.w===this.w&&q.x===this.x&&q.y===this.y&&q.z===this.z};Quaternion.prototype.dot=function dot(q){return this.w*q.w+this.x*q.x+this.y*q.y+this.z*q.z};Quaternion.prototype.normSquared=function normSquared(){return this.dot(this)};Quaternion.prototype.norm=function norm(){return Math.sqrt(this.normSquared())};Quaternion.prototype.isZero=function isZero(){return!(this.x||this.y||this.z)};Quaternion.prototype.getTransform=function getTransform(){var temp=this.normalize(1);var x=temp.x;var y=temp.y;var z=temp.z;var w=temp.w;return[1-2*y*y-2*z*z,2*x*y-2*z*w,2*x*z+2*y*w,0,2*x*y+2*z*w,1-2*x*x-2*z*z,2*y*z-2*x*w,0,2*x*z-2*y*w,2*y*z+2*x*w,1-2*x*x-2*y*y,0,0,0,0,1]};var matrixRegister=new Matrix;Quaternion.prototype.getMatrix=function getMatrix(){var temp=this.normalize(1);var x=temp.x;var y=temp.y;var z=temp.z;var w=temp.w;return matrixRegister.set([[1-2*y*y-2*z*z,2*x*y+2*z*w,2*x*z-2*y*w],[2*x*y-2*z*w,1-2*x*x-2*z*z,2*y*z+2*x*w],[2*x*z+2*y*w,2*y*z-2*x*w,1-2*x*x-2*y*y]])};var epsilon=1e-5;Quaternion.prototype.slerp=function slerp(q,t){var omega;var cosomega;var sinomega;var scaleFrom;var scaleTo;cosomega=this.dot(q);if(1-cosomega>epsilon){omega=Math.acos(cosomega);sinomega=Math.sin(omega);scaleFrom=Math.sin((1-t)*omega)/sinomega;scaleTo=Math.sin(t*omega)/sinomega}else{scaleFrom=1-t;scaleTo=t}return register.set(this.scalarMultiply(scaleFrom/scaleTo).add(q).multiply(scaleTo))};module.exports=Quaternion});define("famous/math/Random",["require","exports","module"],function(require,exports,module){var RAND=Math.random;function _randomFloat(min,max){return min+RAND()*(max-min)}function _randomInteger(min,max){return min+RAND()*(max-min+1)>>0}var Random={};Random.integer=function integer(min,max,dim){min=min!==undefined?min:0;max=max!==undefined?max:1;if(dim!==undefined){var result=[];for(var i=0;i<dim;i++)result.push(_randomInteger(min,max));return result}else return _randomInteger(min,max)};Random.range=function range(min,max,dim){min=min!==undefined?min:0;max=max!==undefined?max:1;if(dim!==undefined){var result=[];for(var i=0;i<dim;i++)result.push(_randomFloat(min,max));return result}else return _randomFloat(min,max)};Random.sign=function sign(prob){prob=prob!==undefined?prob:.5;return RAND()<prob?1:-1};Random.bool=function bool(prob){prob=prob!==undefined?prob:.5;return RAND()<prob};module.exports=Random});define("famous/events/EventArbiter",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function EventArbiter(startMode){this.dispatchers={};this.currMode=undefined;this.setMode(startMode)}EventArbiter.prototype.setMode=function setMode(mode){if(mode!==this.currMode){var startMode=this.currMode;if(this.dispatchers[this.currMode])this.dispatchers[this.currMode].trigger("unpipe");this.currMode=mode;if(this.dispatchers[mode])this.dispatchers[mode].emit("pipe");this.emit("change",{from:startMode,to:mode})}};EventArbiter.prototype.forMode=function forMode(mode){if(!this.dispatchers[mode])this.dispatchers[mode]=new EventHandler;return this.dispatchers[mode]};EventArbiter.prototype.emit=function emit(eventType,event){if(this.currMode===undefined)return false;if(!event)event={};var dispatcher=this.dispatchers[this.currMode];if(dispatcher)return dispatcher.trigger(eventType,event)};module.exports=EventArbiter});define("famous/events/EventFilter",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function EventFilter(condition){EventHandler.call(this);this._condition=condition}EventFilter.prototype=Object.create(EventHandler.prototype);EventFilter.prototype.constructor=EventFilter;EventFilter.prototype.emit=function emit(type,data){if(this._condition(type,data))return EventHandler.prototype.emit.apply(this,arguments)};EventFilter.prototype.trigger=EventFilter.prototype.emit;module.exports=EventFilter});define("famous/events/EventMapper",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function EventMapper(mappingFunction){EventHandler.call(this);this._mappingFunction=mappingFunction}EventMapper.prototype=Object.create(EventHandler.prototype);EventMapper.prototype.constructor=EventMapper;EventMapper.prototype.subscribe=null;EventMapper.prototype.unsubscribe=null;EventMapper.prototype.emit=function emit(type,data){var target=this._mappingFunction.apply(this,arguments);if(target&&target.emit instanceof Function)target.emit(type,data)};EventMapper.prototype.trigger=EventMapper.prototype.emit;module.exports=EventMapper});define("famous/physics/PhysicsEngine",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function PhysicsEngine(options){this.options=Object.create(PhysicsEngine.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._particles=[];this._bodies=[];this._agents={};this._forces=[];this._constraints=[];this._buffer=0;this._prevTime=now();this._isSleeping=false;this._eventHandler=null;this._currAgentId=0;this._hasBodies=false}var TIMESTEP=17;var MIN_TIME_STEP=1e3/120;var MAX_TIME_STEP=17;PhysicsEngine.DEFAULT_OPTIONS={constraintSteps:1,sleepTolerance:1e-7};var now=function(){return Date.now}();PhysicsEngine.prototype.setOptions=function setOptions(opts){for(var key in opts)if(this.options[key])this.options[key]=opts[key]};PhysicsEngine.prototype.addBody=function addBody(body){body._engine=this;if(body.isBody){this._bodies.push(body);this._hasBodies=true}else this._particles.push(body);return body};PhysicsEngine.prototype.removeBody=function removeBody(body){var array=body.isBody?this._bodies:this._particles;var index=array.indexOf(body);if(index>-1){for(var i=0;i<Object.keys(this._agents).length;i++)this.detachFrom(i,body);array.splice(index,1)}if(this.getBodies().length===0)this._hasBodies=false};function _mapAgentArray(agent){if(agent.applyForce)return this._forces;if(agent.applyConstraint)return this._constraints}function _attachOne(agent,targets,source){if(targets===undefined)targets=this.getParticlesAndBodies();if(!(targets instanceof Array))targets=[targets];this._agents[this._currAgentId]={agent:agent,targets:targets,source:source};_mapAgentArray.call(this,agent).push(this._currAgentId);return this._currAgentId++}PhysicsEngine.prototype.attach=function attach(agents,targets,source){if(agents instanceof Array){var agentIDs=[];for(var i=0;i<agents.length;i++)agentIDs[i]=_attachOne.call(this,agents[i],targets,source);return agentIDs}else return _attachOne.call(this,agents,targets,source)};PhysicsEngine.prototype.attachTo=function attachTo(agentID,target){_getBoundAgent.call(this,agentID).targets.push(target)};PhysicsEngine.prototype.detach=function detach(id){var agent=this.getAgent(id);var agentArray=_mapAgentArray.call(this,agent);var index=agentArray.indexOf(id);agentArray.splice(index,1);delete this._agents[id]};PhysicsEngine.prototype.detachFrom=function detachFrom(id,target){var boundAgent=_getBoundAgent.call(this,id);if(boundAgent.source===target)this.detach(id);else{var targets=boundAgent.targets;var index=targets.indexOf(target);if(index>-1)targets.splice(index,1)}};PhysicsEngine.prototype.detachAll=function detachAll(){this._agents={};this._forces=[];this._constraints=[];this._currAgentId=0};function _getBoundAgent(id){return this._agents[id]}PhysicsEngine.prototype.getAgent=function getAgent(id){return _getBoundAgent.call(this,id).agent};PhysicsEngine.prototype.getParticles=function getParticles(){return this._particles};PhysicsEngine.prototype.getBodies=function getBodies(){return this._bodies};PhysicsEngine.prototype.getParticlesAndBodies=function getParticlesAndBodies(){return this.getParticles().concat(this.getBodies())};PhysicsEngine.prototype.forEachParticle=function forEachParticle(fn,dt){var particles=this.getParticles();for(var index=0,len=particles.length;index<len;index++)fn.call(this,particles[index],dt)};PhysicsEngine.prototype.forEachBody=function forEachBody(fn,dt){if(!this._hasBodies)return;var bodies=this.getBodies();for(var index=0,len=bodies.length;index<len;index++)fn.call(this,bodies[index],dt)};PhysicsEngine.prototype.forEach=function forEach(fn,dt){this.forEachParticle(fn,dt);this.forEachBody(fn,dt)};function _updateForce(index){var boundAgent=_getBoundAgent.call(this,this._forces[index]);boundAgent.agent.applyForce(boundAgent.targets,boundAgent.source)}function _updateForces(){for(var index=this._forces.length-1;index>-1;index--)_updateForce.call(this,index)}function _updateConstraint(index,dt){var boundAgent=this._agents[this._constraints[index]];return boundAgent.agent.applyConstraint(boundAgent.targets,boundAgent.source,dt)}function _updateConstraints(dt){var iteration=0;while(iteration<this.options.constraintSteps){for(var index=this._constraints.length-1;index>-1;index--)_updateConstraint.call(this,index,dt);iteration++}}function _updateVelocities(particle,dt){particle.integrateVelocity(dt)}function _updateAngularVelocities(body,dt){body.integrateAngularMomentum(dt);body.updateAngularVelocity()}function _updateOrientations(body,dt){body.integrateOrientation(dt)}function _updatePositions(particle,dt){particle.integratePosition(dt);particle.emit("update",particle)}function _integrate(dt){_updateForces.call(this,dt);this.forEach(_updateVelocities,dt);this.forEachBody(_updateAngularVelocities,dt);_updateConstraints.call(this,dt);this.forEachBody(_updateOrientations,dt);this.forEach(_updatePositions,dt)}function _getEnergyParticles(){var energy=0;var particleEnergy=0;this.forEach(function(particle){particleEnergy=particle.getEnergy();energy+=particleEnergy;if(particleEnergy<particle.sleepTolerance)particle.sleep()});return energy}function _getEnergyForces(){var energy=0;for(var index=this._forces.length-1;index>-1;index--)energy+=this._forces[index].getEnergy()||0;return energy}function _getEnergyConstraints(){var energy=0;for(var index=this._constraints.length-1;index>-1;index--)energy+=this._constraints[index].getEnergy()||0;return energy}PhysicsEngine.prototype.getEnergy=function getEnergy(){return _getEnergyParticles.call(this)+_getEnergyForces.call(this)+_getEnergyConstraints.call(this)};PhysicsEngine.prototype.step=function step(){var currTime=now();var dtFrame=currTime-this._prevTime;this._prevTime=currTime;if(dtFrame<MIN_TIME_STEP)return;if(dtFrame>MAX_TIME_STEP)dtFrame=MAX_TIME_STEP;_integrate.call(this,TIMESTEP)};PhysicsEngine.prototype.isSleeping=function isSleeping(){return this._isSleeping};PhysicsEngine.prototype.sleep=function sleep(){this.emit("end",this);this._isSleeping=true};PhysicsEngine.prototype.wake=function wake(){this._prevTime=now();this.emit("start",this);this._isSleeping=false};PhysicsEngine.prototype.emit=function emit(type,data){if(this._eventHandler===null)return;this._eventHandler.emit(type,data)};PhysicsEngine.prototype.on=function on(event,fn){if(this._eventHandler===null)this._eventHandler=new EventHandler;this._eventHandler.on(event,fn)};module.exports=PhysicsEngine});define("famous/inputs/Accumulator",["require","exports","module","famous/core/EventHandler","famous/transitions/Transitionable"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");var Transitionable=require("famous/transitions/Transitionable");function Accumulator(value,eventName){if(eventName===undefined)eventName="update";this._state=value&&value.get&&value.set?value:new Transitionable(value||0);this._eventInput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);this._eventInput.on(eventName,_handleUpdate.bind(this))}function _handleUpdate(data){var delta=data.delta;var state=this.get();if(delta.constructor===state.constructor){var newState=delta instanceof Array?[state[0]+delta[0],state[1]+delta[1]]:state+delta;this.set(newState)}}Accumulator.prototype.get=function get(){return this._state.get()};Accumulator.prototype.set=function set(value){this._state.set(value)};module.exports=Accumulator});define("famous/inputs/DesktopEmulationMode",["require","exports","module"],function(require,exports,module){var hasTouch="ontouchstart"in window;function kill(type){window.addEventListener(type,function(event){event.stopPropagation();return false},true)}if(hasTouch){kill("mousedown");kill("mousemove");kill("mouseup");kill("mouseleave")}});define("famous/inputs/FastClick",["require","exports","module"],function(require,exports,module){if(!window.CustomEvent)return;var clickThreshold=300;var clickWindow=500;var potentialClicks={};var recentlyDispatched={};var _now=Date.now;window.addEventListener("touchstart",function(event){var timestamp=_now();for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];potentialClicks[touch.identifier]=timestamp}});window.addEventListener("touchmove",function(event){for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];delete potentialClicks[touch.identifier]}});window.addEventListener("touchend",function(event){var currTime=_now();for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];var startTime=potentialClicks[touch.identifier];if(startTime&&currTime-startTime<clickThreshold){var clickEvt=new window.CustomEvent("click",{bubbles:true,details:touch});recentlyDispatched[currTime]=event;event.target.dispatchEvent(clickEvt)}delete potentialClicks[touch.identifier]}});window.addEventListener("click",function(event){var currTime=_now();for(var i in recentlyDispatched){var previousEvent=recentlyDispatched[i];if(currTime-i<clickWindow){if(event instanceof window.MouseEvent&&event.target===previousEvent.target)event.stopPropagation()}else delete recentlyDispatched[i]}},true)});define("famous/inputs/TwoFingerSync",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function TwoFingerSync(){this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this.touchAEnabled=false;this.touchAId=0;this.posA=null; | |
this.timestampA=0;this.touchBEnabled=false;this.touchBId=0;this.posB=null;this.timestampB=0;this._eventInput.on("touchstart",this.handleStart.bind(this));this._eventInput.on("touchmove",this.handleMove.bind(this));this._eventInput.on("touchend",this.handleEnd.bind(this));this._eventInput.on("touchcancel",this.handleEnd.bind(this))}TwoFingerSync.calculateAngle=function(posA,posB){var diffX=posB[0]-posA[0];var diffY=posB[1]-posA[1];return Math.atan2(diffY,diffX)};TwoFingerSync.calculateDistance=function(posA,posB){var diffX=posB[0]-posA[0];var diffY=posB[1]-posA[1];return Math.sqrt(diffX*diffX+diffY*diffY)};TwoFingerSync.calculateCenter=function(posA,posB){return[(posA[0]+posB[0])/2,(posA[1]+posB[1])/2]};var _now=Date.now;TwoFingerSync.prototype.handleStart=function handleStart(event){for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];if(!this.touchAEnabled){this.touchAId=touch.identifier;this.touchAEnabled=true;this.posA=[touch.pageX,touch.pageY];this.timestampA=_now()}else if(!this.touchBEnabled){this.touchBId=touch.identifier;this.touchBEnabled=true;this.posB=[touch.pageX,touch.pageY];this.timestampB=_now();this._startUpdate(event)}}};TwoFingerSync.prototype.handleMove=function handleMove(event){if(!(this.touchAEnabled&&this.touchBEnabled))return;var prevTimeA=this.timestampA;var prevTimeB=this.timestampB;var diffTime;for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];if(touch.identifier===this.touchAId){this.posA=[touch.pageX,touch.pageY];this.timestampA=_now();diffTime=this.timestampA-prevTimeA}else if(touch.identifier===this.touchBId){this.posB=[touch.pageX,touch.pageY];this.timestampB=_now();diffTime=this.timestampB-prevTimeB}}if(diffTime)this._moveUpdate(diffTime)};TwoFingerSync.prototype.handleEnd=function handleEnd(event){for(var i=0;i<event.changedTouches.length;i++){var touch=event.changedTouches[i];if(touch.identifier===this.touchAId||touch.identifier===this.touchBId){if(this.touchAEnabled&&this.touchBEnabled){this._eventOutput.emit("end",{touches:[this.touchAId,this.touchBId],angle:this._angle})}this.touchAEnabled=false;this.touchAId=0;this.touchBEnabled=false;this.touchBId=0}}};module.exports=TwoFingerSync});define("famous/inputs/PinchSync",["require","exports","module","./TwoFingerSync"],function(require,exports,module){var TwoFingerSync=require("./TwoFingerSync");function PinchSync(options){TwoFingerSync.call(this);this.options=Object.create(PinchSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._displacement=0;this._previousDistance=0}PinchSync.prototype=Object.create(TwoFingerSync.prototype);PinchSync.prototype.constructor=PinchSync;PinchSync.DEFAULT_OPTIONS={scale:1};PinchSync.prototype._startUpdate=function _startUpdate(event){this._previousDistance=TwoFingerSync.calculateDistance(this.posA,this.posB);this._displacement=0;this._eventOutput.emit("start",{count:event.touches.length,touches:[this.touchAId,this.touchBId],distance:this._dist,center:TwoFingerSync.calculateCenter(this.posA,this.posB)})};PinchSync.prototype._moveUpdate=function _moveUpdate(diffTime){var currDist=TwoFingerSync.calculateDistance(this.posA,this.posB);var center=TwoFingerSync.calculateCenter(this.posA,this.posB);var scale=this.options.scale;var delta=scale*(currDist-this._previousDistance);var velocity=delta/diffTime;this._previousDistance=currDist;this._displacement+=delta;this._eventOutput.emit("update",{delta:delta,velocity:velocity,distance:currDist,displacement:this._displacement,center:center,touches:[this.touchAId,this.touchBId]})};PinchSync.prototype.getOptions=function getOptions(){return this.options};PinchSync.prototype.setOptions=function setOptions(options){if(options.scale!==undefined)this.options.scale=options.scale};module.exports=PinchSync});define("famous/inputs/RotateSync",["require","exports","module","./TwoFingerSync"],function(require,exports,module){var TwoFingerSync=require("./TwoFingerSync");function RotateSync(options){TwoFingerSync.call(this);this.options=Object.create(RotateSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._angle=0;this._previousAngle=0}RotateSync.prototype=Object.create(TwoFingerSync.prototype);RotateSync.prototype.constructor=RotateSync;RotateSync.DEFAULT_OPTIONS={scale:1};RotateSync.prototype._startUpdate=function _startUpdate(event){this._angle=0;this._previousAngle=TwoFingerSync.calculateAngle(this.posA,this.posB);var center=TwoFingerSync.calculateCenter(this.posA,this.posB);this._eventOutput.emit("start",{count:event.touches.length,angle:this._angle,center:center,touches:[this.touchAId,this.touchBId]})};RotateSync.prototype._moveUpdate=function _moveUpdate(diffTime){var scale=this.options.scale;var currAngle=TwoFingerSync.calculateAngle(this.posA,this.posB);var center=TwoFingerSync.calculateCenter(this.posA,this.posB);var diffTheta=scale*(currAngle-this._previousAngle);var velTheta=diffTheta/diffTime;this._angle+=diffTheta;this._eventOutput.emit("update",{delta:diffTheta,velocity:velTheta,angle:this._angle,center:center,touches:[this.touchAId,this.touchBId]});this._previousAngle=currAngle};RotateSync.prototype.getOptions=function getOptions(){return this.options};RotateSync.prototype.setOptions=function setOptions(options){if(options.scale!==undefined)this.options.scale=options.scale};module.exports=RotateSync});define("famous/inputs/ScaleSync",["require","exports","module","./TwoFingerSync"],function(require,exports,module){var TwoFingerSync=require("./TwoFingerSync");function ScaleSync(options){TwoFingerSync.call(this);this.options=Object.create(ScaleSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._scaleFactor=1;this._startDist=0;this._eventInput.on("pipe",_reset.bind(this))}ScaleSync.prototype=Object.create(TwoFingerSync.prototype);ScaleSync.prototype.constructor=ScaleSync;ScaleSync.DEFAULT_OPTIONS={scale:1};function _reset(){this.touchAId=undefined;this.touchBId=undefined}ScaleSync.prototype._startUpdate=function _startUpdate(event){this._scaleFactor=1;this._startDist=TwoFingerSync.calculateDistance(this.posA,this.posB);this._eventOutput.emit("start",{count:event.touches.length,touches:[this.touchAId,this.touchBId],distance:this._startDist,center:TwoFingerSync.calculateCenter(this.posA,this.posB)})};ScaleSync.prototype._moveUpdate=function _moveUpdate(diffTime){var scale=this.options.scale;var currDist=TwoFingerSync.calculateDistance(this.posA,this.posB);var center=TwoFingerSync.calculateCenter(this.posA,this.posB);var delta=(currDist-this._startDist)/this._startDist;var newScaleFactor=Math.max(1+scale*delta,0);var veloScale=(newScaleFactor-this._scaleFactor)/diffTime;this._eventOutput.emit("update",{delta:delta,scale:newScaleFactor,velocity:veloScale,distance:currDist,center:center,touches:[this.touchAId,this.touchBId]});this._scaleFactor=newScaleFactor};ScaleSync.prototype.getOptions=function getOptions(){return this.options};ScaleSync.prototype.setOptions=function setOptions(options){if(options.scale!==undefined)this.options.scale=options.scale};module.exports=ScaleSync});define("famous/inputs/ScrollSync",["require","exports","module","famous/core/EventHandler","famous/core/Engine"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");var Engine=require("famous/core/Engine");function ScrollSync(options){this.options=Object.create(ScrollSync.DEFAULT_OPTIONS);if(options)this.setOptions(options);this._payload={delta:null,position:null,velocity:null,slip:true};this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this._position=this.options.direction===undefined?[0,0]:0;this._prevTime=undefined;this._prevVel=undefined;this._eventInput.on("mousewheel",_handleMove.bind(this));this._eventInput.on("wheel",_handleMove.bind(this));this._inProgress=false;this._loopBound=false}ScrollSync.DEFAULT_OPTIONS={direction:undefined,minimumEndSpeed:Infinity,rails:false,scale:1,stallTime:50,lineHeight:40};ScrollSync.DIRECTION_X=0;ScrollSync.DIRECTION_Y=1;var MINIMUM_TICK_TIME=8;var _now=Date.now;function _newFrame(){if(this._inProgress&&_now()-this._prevTime>this.options.stallTime){this._position=this.options.direction===undefined?[0,0]:0;this._inProgress=false;var finalVel=Math.abs(this._prevVel)>=this.options.minimumEndSpeed?this._prevVel:0;var payload=this._payload;payload.position=this._position;payload.velocity=finalVel;payload.slip=true;this._eventOutput.emit("end",payload)}}function _handleMove(event){event.preventDefault();if(!this._inProgress){this._inProgress=true;payload=this._payload;payload.slip=true;payload.position=this._position;payload.clientX=event.clientX;payload.clientY=event.clientY;payload.offsetX=event.offsetX;payload.offsetY=event.offsetY;this._eventOutput.emit("start",payload);if(!this._loopBound){Engine.on("prerender",_newFrame.bind(this));this._loopBound=true}}var currTime=_now();var prevTime=this._prevTime||currTime;var diffX=event.wheelDeltaX!==undefined?event.wheelDeltaX:-event.deltaX;var diffY=event.wheelDeltaY!==undefined?event.wheelDeltaY:-event.deltaY;if(event.deltaMode===1){diffX*=this.options.lineHeight;diffY*=this.options.lineHeight}if(this.options.rails){if(Math.abs(diffX)>Math.abs(diffY))diffY=0;else diffX=0}var diffTime=Math.max(currTime-prevTime,MINIMUM_TICK_TIME);var velX=diffX/diffTime;var velY=diffY/diffTime;var scale=this.options.scale;var nextVel;var nextDelta;if(this.options.direction===ScrollSync.DIRECTION_X){nextDelta=scale*diffX;nextVel=scale*velX;this._position+=nextDelta}else if(this.options.direction===ScrollSync.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.slip=true;this._eventOutput.emit("update",payload);this._prevTime=currTime;this._prevVel=nextVel}ScrollSync.prototype.getOptions=function getOptions(){return this.options};ScrollSync.prototype.setOptions=function setOptions(options){if(options.direction!==undefined)this.options.direction=options.direction;if(options.minimumEndSpeed!==undefined)this.options.minimumEndSpeed=options.minimumEndSpeed;if(options.rails!==undefined)this.options.rails=options.rails;if(options.scale!==undefined)this.options.scale=options.scale;if(options.stallTime!==undefined)this.options.stallTime=options.stallTime};module.exports=ScrollSync});define("famous/transitions/CachedMap",["require","exports","module"],function(require,exports,module){function CachedMap(mappingFunction){this._map=mappingFunction||null;this._cachedOutput=null;this._cachedInput=Number.NaN}CachedMap.create=function create(mappingFunction){var instance=new CachedMap(mappingFunction);return instance.get.bind(instance)};CachedMap.prototype.get=function get(input){if(input!==this._cachedInput){this._cachedInput=input;this._cachedOutput=this._map(input)}return this._cachedOutput};module.exports=CachedMap});define("famous/transitions/Easing",["require","exports","module"],function(require,exports,module){var Easing={inQuad:function(t){return t*t},outQuad:function(t){return-(t-=1)*t+1},inOutQuad:function(t){if((t/=.5)<1)return.5*t*t;return-.5*(--t*(t-2)-1)},inCubic:function(t){return t*t*t},outCubic:function(t){return--t*t*t+1},inOutCubic:function(t){if((t/=.5)<1)return.5*t*t*t;return.5*((t-=2)*t*t+2)},inQuart:function(t){return t*t*t*t},outQuart:function(t){return-(--t*t*t*t-1)},inOutQuart:function(t){if((t/=.5)<1)return.5*t*t*t*t;return-.5*((t-=2)*t*t*t-2)},inQuint:function(t){return t*t*t*t*t},outQuint:function(t){return--t*t*t*t*t+1},inOutQuint:function(t){if((t/=.5)<1)return.5*t*t*t*t*t;return.5*((t-=2)*t*t*t*t+2)},inSine:function(t){return-1*Math.cos(t*(Math.PI/2))+1},outSine:function(t){return Math.sin(t*(Math.PI/2))},inOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},inExpo:function(t){return t===0?0:Math.pow(2,10*(t-1))},outExpo:function(t){return t===1?1:-Math.pow(2,-10*t)+1},inOutExpo:function(t){if(t===0)return 0;if(t===1)return 1;if((t/=.5)<1)return.5*Math.pow(2,10*(t-1));return.5*(-Math.pow(2,-10*--t)+2)},inCirc:function(t){return-(Math.sqrt(1-t*t)-1)},outCirc:function(t){return Math.sqrt(1- --t*t)},inOutCirc:function(t){if((t/=.5)<1)return-.5*(Math.sqrt(1-t*t)-1);return.5*(Math.sqrt(1-(t-=2)*t)+1)},inElastic:function(t){var s=1.70158;var p=0;var a=1;if(t===0)return 0;if(t===1)return 1;if(!p)p=.3;s=p/(2*Math.PI)*Math.asin(1/a);return-(a*Math.pow(2,10*(t-=1))*Math.sin((t-s)*(2*Math.PI)/p))},outElastic:function(t){var s=1.70158;var p=0;var a=1;if(t===0)return 0;if(t===1)return 1;if(!p)p=.3;s=p/(2*Math.PI)*Math.asin(1/a);return a*Math.pow(2,-10*t)*Math.sin((t-s)*(2*Math.PI)/p)+1},inOutElastic:function(t){var s=1.70158;var p=0;var a=1;if(t===0)return 0;if((t/=.5)===2)return 1;if(!p)p=.3*1.5;s=p/(2*Math.PI)*Math.asin(1/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},inBack:function(t,s){if(s===undefined)s=1.70158;return t*t*((s+1)*t-s)},outBack:function(t,s){if(s===undefined)s=1.70158;return--t*t*((s+1)*t+s)+1},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)},inBounce:function(t){return 1-Easing.outBounce(1-t)},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}},inOutBounce:function(t){if(t<.5)return Easing.inBounce(t*2)*.5;return Easing.outBounce(t*2-1)*.5+.5}};module.exports=Easing});define("famous/physics/integrators/SymplecticEuler",["require","exports","module","famous/core/OptionsManager"],function(require,exports,module){var OptionsManager=require("famous/core/OptionsManager");function SymplecticEuler(options){this.options=Object.create(SymplecticEuler.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options)}SymplecticEuler.DEFAULT_OPTIONS={velocityCap:undefined,angularVelocityCap:undefined};SymplecticEuler.prototype.setOptions=function setOptions(options){this._optionsManager.patch(options)};SymplecticEuler.prototype.getOptions=function getOptions(){return this._optionsManager.value()};SymplecticEuler.prototype.integrateVelocity=function integrateVelocity(body,dt){var v=body.velocity;var w=body.inverseMass;var f=body.force;if(f.isZero())return;v.add(f.mult(dt*w)).put(v);f.clear()};SymplecticEuler.prototype.integratePosition=function integratePosition(body,dt){var p=body.position;var v=body.velocity;if(this.options.velocityCap)v.cap(this.options.velocityCap).put(v);p.add(v.mult(dt)).put(p)};SymplecticEuler.prototype.integrateAngularMomentum=function integrateAngularMomentum(body,dt){var L=body.angularMomentum;var t=body.torque;if(t.isZero())return;if(this.options.angularVelocityCap)t.cap(this.options.angularVelocityCap).put(t);L.add(t.mult(dt)).put(L);t.clear()};SymplecticEuler.prototype.integrateOrientation=function integrateOrientation(body,dt){var q=body.orientation;var w=body.angularVelocity;if(w.isZero())return;q.add(q.multiply(w).scalarMultiply(.5*dt)).put(q)};module.exports=SymplecticEuler});define("famous/physics/bodies/Particle",["require","exports","module","famous/math/Vector","famous/core/Transform","famous/core/EventHandler","../integrators/SymplecticEuler"],function(require,exports,module){var Vector=require("famous/math/Vector");var Transform=require("famous/core/Transform");var EventHandler=require("famous/core/EventHandler");var Integrator=require("../integrators/SymplecticEuler");function Particle(options){options=options||{};this.position=new Vector;this.velocity=new Vector;this.force=new Vector;var defaults=Particle.DEFAULT_OPTIONS;this.setPosition(options.position||defaults.position);this.setVelocity(options.velocity||defaults.velocity);this.force.set(options.force||[0,0,0]);this.mass=options.mass!==undefined?options.mass:defaults.mass;this.axis=options.axis!==undefined?options.axis:defaults.axis;this.inverseMass=1/this.mass;this._isSleeping=false;this._engine=null;this._eventOutput=null;this._positionGetter=null;this.transform=Transform.identity.slice();this._spec={transform:this.transform,target:null}}Particle.DEFAULT_OPTIONS={position:[0,0,0],velocity:[0,0,0],mass:1,axis:undefined};Particle.SLEEP_TOLERANCE=1e-7;Particle.AXES={X:0,Y:1,Z:2};Particle.INTEGRATOR=new Integrator;var _events={start:"start",update:"update",end:"end"};var now=function(){return Date.now}();Particle.prototype.sleep=function sleep(){if(this._isSleeping)return;this.emit(_events.end,this);this._isSleeping=true};Particle.prototype.wake=function wake(){if(!this._isSleeping)return;this.emit(_events.start,this);this._isSleeping=false;this._prevTime=now()};Particle.prototype.isBody=false;Particle.prototype.setPosition=function setPosition(position){this.position.set(position)};Particle.prototype.setPosition1D=function setPosition1D(x){this.position.x=x};Particle.prototype.getPosition=function getPosition(){if(this._positionGetter instanceof Function)this.setPosition(this._positionGetter());this._engine.step();return this.position.get()};Particle.prototype.getPosition1D=function getPosition1D(){this._engine.step();return this.position.x};Particle.prototype.positionFrom=function positionFrom(positionGetter){this._positionGetter=positionGetter};Particle.prototype.setVelocity=function setVelocity(velocity){this.velocity.set(velocity);this.wake()};Particle.prototype.setVelocity1D=function setVelocity1D(x){this.velocity.x=x;this.wake()};Particle.prototype.getVelocity=function getVelocity(){return this.velocity.get()};Particle.prototype.getVelocity1D=function getVelocity1D(){return this.velocity.x};Particle.prototype.setMass=function setMass(mass){this.mass=mass;this.inverseMass=1/mass};Particle.prototype.getMass=function getMass(){return this.mass};Particle.prototype.reset=function reset(position,velocity){this.setPosition(position||[0,0,0]);this.setVelocity(velocity||[0,0,0])};Particle.prototype.applyForce=function applyForce(force){if(force.isZero())return;this.force.add(force).put(this.force);this.wake()};Particle.prototype.applyImpulse=function applyImpulse(impulse){if(impulse.isZero())return;var velocity=this.velocity;velocity.add(impulse.mult(this.inverseMass)).put(velocity)};Particle.prototype.integrateVelocity=function integrateVelocity(dt){Particle.INTEGRATOR.integrateVelocity(this,dt)};Particle.prototype.integratePosition=function integratePosition(dt){Particle.INTEGRATOR.integratePosition(this,dt)};Particle.prototype._integrate=function _integrate(dt){this.integrateVelocity(dt);this.integratePosition(dt)};Particle.prototype.getEnergy=function getEnergy(){return.5*this.mass*this.velocity.normSquared()};Particle.prototype.getTransform=function getTransform(){this._engine.step();var position=this.position;var axis=this.axis;var transform=this.transform;if(axis!==undefined){if(axis&~Particle.AXES.X){position.x=0}if(axis&~Particle.AXES.Y){position.y=0}if(axis&~Particle.AXES.Z){position.z=0}}transform[12]=position.x;transform[13]=position.y;transform[14]=position.z;return transform};Particle.prototype.modify=function modify(target){var _spec=this._spec;_spec.transform=this.getTransform();_spec.target=target;return _spec};function _createEventOutput(){this._eventOutput=new EventHandler;this._eventOutput.bindThis(this);EventHandler.setOutputHandler(this,this._eventOutput)}Particle.prototype.emit=function emit(type,data){if(!this._eventOutput)return;this._eventOutput.emit(type,data)};Particle.prototype.on=function on(){_createEventOutput.call(this);return this.on.apply(this,arguments)};Particle.prototype.removeListener=function removeListener(){_createEventOutput.call(this);return this.removeListener.apply(this,arguments)};Particle.prototype.pipe=function pipe(){_createEventOutput.call(this);return this.pipe.apply(this,arguments)};Particle.prototype.unpipe=function unpipe(){_createEventOutput.call(this);return this.unpipe.apply(this,arguments)};module.exports=Particle});define("famous/physics/constraints/Constraint",["require","exports","module","famous/core/EventHandler"],function(require,exports,module){var EventHandler=require("famous/core/EventHandler");function Constraint(){this.options=this.options||{};this._energy=0;this._eventOutput=null}Constraint.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};Constraint.prototype.applyConstraint=function applyConstraint(){};Constraint.prototype.getEnergy=function getEnergy(){return this._energy};Constraint.prototype.setEnergy=function setEnergy(energy){this._energy=energy};function _createEventOutput(){this._eventOutput=new EventHandler;this._eventOutput.bindThis(this);EventHandler.setOutputHandler(this,this._eventOutput)}Constraint.prototype.on=function on(){_createEventOutput.call(this);return this.on.apply(this,arguments)};Constraint.prototype.addListener=function addListener(){_createEventOutput.call(this);return this.addListener.apply(this,arguments)};Constraint.prototype.pipe=function pipe(){_createEventOutput.call(this);return this.pipe.apply(this,arguments)};Constraint.prototype.removeListener=function removeListener(){return this.removeListener.apply(this,arguments)};Constraint.prototype.unpipe=function unpipe(){return this.unpipe.apply(this,arguments)};module.exports=Constraint});define("famous/physics/constraints/Snap",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Snap(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.pDiff=new Vector;this.vDiff=new Vector;this.impulse1=new Vector;this.impulse2=new Vector;Constraint.call(this)}Snap.prototype=Object.create(Constraint.prototype);Snap.prototype.constructor=Snap;Snap.DEFAULT_OPTIONS={period:300,dampingRatio:.1,length:0,anchor:undefined};var pi=Math.PI;function _calcEnergy(impulse,disp,dt){return Math.abs(impulse.dot(disp)/dt)}Snap.prototype.setOptions=function setOptions(options){if(options.anchor!==undefined){if(options.anchor instanceof Vector)this.options.anchor=options.anchor;if(options.anchor.position instanceof Vector)this.options.anchor=options.anchor.position;if(options.anchor instanceof Array)this.options.anchor=new Vector(options.anchor)}if(options.length!==undefined)this.options.length=options.length;if(options.dampingRatio!==undefined)this.options.dampingRatio=options.dampingRatio;if(options.period!==undefined)this.options.period=options.period};Snap.prototype.setAnchor=function setAnchor(v){if(this.options.anchor!==undefined)this.options.anchor=new Vector;this.options.anchor.set(v)};Snap.prototype.getEnergy=function getEnergy(target,source){var options=this.options;var restLength=options.length;var anchor=options.anchor||source.position;var strength=Math.pow(2*pi/options.period,2);var dist=anchor.sub(target.position).norm()-restLength;return.5*strength*dist*dist};Snap.prototype.applyConstraint=function applyConstraint(targets,source,dt){var options=this.options;var pDiff=this.pDiff;var vDiff=this.vDiff;var impulse1=this.impulse1;var impulse2=this.impulse2;var length=options.length;var anchor=options.anchor||source.position;var period=options.period;var dampingRatio=options.dampingRatio;for(var i=0;i<targets.length;i++){var target=targets[i];var p1=target.position;var v1=target.velocity;var m1=target.mass;var w1=target.inverseMass;pDiff.set(p1.sub(anchor));var dist=pDiff.norm()-length;var effMass;if(source){var w2=source.inverseMass;var v2=source.velocity;vDiff.set(v1.sub(v2));effMass=1/(w1+w2)}else{vDiff.set(v1);effMass=m1}var gamma;var beta;if(this.options.period===0){gamma=0;beta=1}else{var k=4*effMass*pi*pi/(period*period);var c=4*effMass*pi*dampingRatio/period;beta=dt*k/(c+dt*k);gamma=1/(c+dt*k)}var antiDrift=beta/dt*dist;pDiff.normalize(-antiDrift).sub(vDiff).mult(dt/(gamma+dt/effMass)).put(impulse1);target.applyImpulse(impulse1);if(source){impulse1.mult(-1).put(impulse2);source.applyImpulse(impulse2)}this.setEnergy(_calcEnergy(impulse1,pDiff,dt))}};module.exports=Snap});define("famous/transitions/SnapTransition",["require","exports","module","famous/physics/PhysicsEngine","famous/physics/bodies/Particle","famous/physics/constraints/Snap","famous/math/Vector"],function(require,exports,module){var PE=require("famous/physics/PhysicsEngine");var Particle=require("famous/physics/bodies/Particle");var Spring=require("famous/physics/constraints/Snap");var Vector=require("famous/math/Vector");function SnapTransition(state){state=state||0;this.endState=new Vector(state);this.initState=new Vector;this._dimensions=1;this._restTolerance=1e-10;this._absRestTolerance=this._restTolerance;this._callback=undefined;this.PE=new PE;this.particle=new Particle;this.spring=new Spring({anchor:this.endState});this.PE.addBody(this.particle);this.PE.attach(this.spring,this.particle)}SnapTransition.SUPPORTS_MULTIPLE=3;SnapTransition.DEFAULT_OPTIONS={period:100,dampingRatio:.2,velocity:0};function _getEnergy(){return this.particle.getEnergy()+this.spring.getEnergy(this.particle)}function _setAbsoluteRestTolerance(){var distance=this.endState.sub(this.initState).normSquared();this._absRestTolerance=distance===0?this._restTolerance:this._restTolerance*distance}function _setTarget(target){this.endState.set(target);_setAbsoluteRestTolerance.call(this)}function _wake(){this.PE.wake()}function _sleep(){this.PE.sleep()}function _setParticlePosition(p){this.particle.position.set(p)}function _setParticleVelocity(v){this.particle.velocity.set(v)}function _getParticlePosition(){return this._dimensions===0?this.particle.getPosition1D():this.particle.getPosition()}function _getParticleVelocity(){return this._dimensions===0?this.particle.getVelocity1D():this.particle.getVelocity()}function _setCallback(callback){this._callback=callback}function _setupDefinition(definition){var defaults=SnapTransition.DEFAULT_OPTIONS;if(definition.period===undefined)definition.period=defaults.period;if(definition.dampingRatio===undefined)definition.dampingRatio=defaults.dampingRatio;if(definition.velocity===undefined)definition.velocity=defaults.velocity;this.spring.setOptions({period:definition.period,dampingRatio:definition.dampingRatio});_setParticleVelocity.call(this,definition.velocity)}function _update(){if(this.PE.isSleeping()){if(this._callback){var cb=this._callback;this._callback=undefined;cb()}return}if(_getEnergy.call(this)<this._absRestTolerance){_setParticlePosition.call(this,this.endState);_setParticleVelocity.call(this,[0,0,0]);_sleep.call(this)}}SnapTransition.prototype.reset=function reset(state,velocity){this._dimensions=state instanceof Array?state.length:0;this.initState.set(state);_setParticlePosition.call(this,state);_setTarget.call(this,state);if(velocity)_setParticleVelocity.call(this,velocity);_setCallback.call(this,undefined)};SnapTransition.prototype.getVelocity=function getVelocity(){return _getParticleVelocity.call(this)};SnapTransition.prototype.setVelocity=function setVelocity(velocity){this.call(this,_setParticleVelocity(velocity))};SnapTransition.prototype.isActive=function isActive(){return!this.PE.isSleeping()};SnapTransition.prototype.halt=function halt(){this.set(this.get())};SnapTransition.prototype.get=function get(){_update.call(this);return _getParticlePosition.call(this)};SnapTransition.prototype.set=function set(state,definition,callback){if(!definition){this.reset(state);if(callback)callback();return}this._dimensions=state instanceof Array?state.length:0;_wake.call(this);_setupDefinition.call(this,definition);_setTarget.call(this,state);_setCallback.call(this,callback)};module.exports=SnapTransition});define("famous/physics/forces/Force",["require","exports","module","famous/math/Vector","famous/core/EventHandler"],function(require,exports,module){var Vector=require("famous/math/Vector");var EventHandler=require("famous/core/EventHandler");function Force(force){this.force=new Vector(force);this._energy=0;this._eventOutput=null}Force.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};Force.prototype.applyForce=function applyForce(body){body.applyForce(this.force)};Force.prototype.getEnergy=function getEnergy(){return this._energy};Force.prototype.setEnergy=function setEnergy(energy){this._energy=energy};function _createEventOutput(){this._eventOutput=new EventHandler;this._eventOutput.bindThis(this);EventHandler.setOutputHandler(this,this._eventOutput)}Force.prototype.on=function on(){_createEventOutput.call(this);return this.on.apply(this,arguments)};Force.prototype.addListener=function addListener(){_createEventOutput.call(this);return this.addListener.apply(this,arguments)};Force.prototype.pipe=function pipe(){_createEventOutput.call(this);return this.pipe.apply(this,arguments)};Force.prototype.removeListener=function removeListener(){return this.removeListener.apply(this,arguments)};Force.prototype.unpipe=function unpipe(){return this.unpipe.apply(this,arguments)};module.exports=Force});define("famous/physics/forces/Spring",["require","exports","module","./Force","famous/math/Vector"],function(require,exports,module){var Force=require("./Force");var Vector=require("famous/math/Vector");function Spring(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.disp=new Vector(0,0,0);_init.call(this);Force.call(this)}Spring.prototype=Object.create(Force.prototype);Spring.prototype.constructor=Spring;var pi=Math.PI;Spring.FORCE_FUNCTIONS={FENE:function(dist,rMax){var rMaxSmall=rMax*.99;var r=Math.max(Math.min(dist,rMaxSmall),-rMaxSmall);return r/(1-r*r/(rMax*rMax))},HOOK:function(dist){return dist}};Spring.DEFAULT_OPTIONS={period:300,dampingRatio:.1,length:0,maxLength:Infinity,anchor:undefined,forceFunction:Spring.FORCE_FUNCTIONS.HOOK};function _setForceFunction(fn){this.forceFunction=fn}function _calcStiffness(){var options=this.options;options.stiffness=Math.pow(2*pi/options.period,2)}function _calcDamping(){var options=this.options;options.damping=4*pi*options.dampingRatio/options.period}function _calcEnergy(strength,dist){return.5*strength*dist*dist}function _init(){_setForceFunction.call(this,this.options.forceFunction);_calcStiffness.call(this);_calcDamping.call(this)}Spring.prototype.setOptions=function setOptions(options){if(options.anchor!==undefined){if(options.anchor.position instanceof Vector)this.options.anchor=options.anchor.position;if(options.anchor instanceof Vector)this.options.anchor=options.anchor;if(options.anchor instanceof Array)this.options.anchor=new Vector(options.anchor)}if(options.period!==undefined)this.options.period=options.period;if(options.dampingRatio!==undefined)this.options.dampingRatio=options.dampingRatio;if(options.length!==undefined)this.options.length=options.length;if(options.forceFunction!==undefined)this.options.forceFunction=options.forceFunction;if(options.maxLength!==undefined)this.options.maxLength=options.maxLength;_init.call(this)};Spring.prototype.applyForce=function applyForce(targets,source){var force=this.force;var disp=this.disp;var options=this.options;var stiffness=options.stiffness;var damping=options.damping;var restLength=options.length;var lMax=options.maxLength;var anchor=options.anchor||source.position;for(var i=0;i<targets.length;i++){var target=targets[i];var p2=target.position;var v2=target.velocity;anchor.sub(p2).put(disp);var dist=disp.norm()-restLength;if(dist===0)return;var m=target.mass;stiffness*=m;damping*=m;disp.normalize(stiffness*this.forceFunction(dist,lMax)).put(force);if(damping)if(source)force.add(v2.sub(source.velocity).mult(-damping)).put(force); | |
else force.add(v2.mult(-damping)).put(force);target.applyForce(force);if(source)source.applyForce(force.mult(-1));this.setEnergy(_calcEnergy(stiffness,dist))}};Spring.prototype.getEnergy=function getEnergy(target){var options=this.options;var restLength=options.length;var anchor=options.anchor;var strength=options.stiffness;var dist=anchor.sub(target.position).norm()-restLength;return.5*strength*dist*dist};Spring.prototype.setAnchor=function setAnchor(anchor){if(!this.options.anchor)this.options.anchor=new Vector;this.options.anchor.set(anchor)};module.exports=Spring});define("famous/transitions/SpringTransition",["require","exports","module","famous/physics/PhysicsEngine","famous/physics/bodies/Particle","famous/physics/forces/Spring","famous/math/Vector"],function(require,exports,module){var PE=require("famous/physics/PhysicsEngine");var Particle=require("famous/physics/bodies/Particle");var Spring=require("famous/physics/forces/Spring");var Vector=require("famous/math/Vector");function SpringTransition(state){state=state||0;this.endState=new Vector(state);this.initState=new Vector;this._dimensions=undefined;this._restTolerance=1e-10;this._absRestTolerance=this._restTolerance;this._callback=undefined;this.PE=new PE;this.spring=new Spring({anchor:this.endState});this.particle=new Particle;this.PE.addBody(this.particle);this.PE.attach(this.spring,this.particle)}SpringTransition.SUPPORTS_MULTIPLE=3;SpringTransition.DEFAULT_OPTIONS={period:300,dampingRatio:.5,velocity:0};function _getEnergy(){return this.particle.getEnergy()+this.spring.getEnergy(this.particle)}function _setParticlePosition(p){this.particle.setPosition(p)}function _setParticleVelocity(v){this.particle.setVelocity(v)}function _getParticlePosition(){return this._dimensions===0?this.particle.getPosition1D():this.particle.getPosition()}function _getParticleVelocity(){return this._dimensions===0?this.particle.getVelocity1D():this.particle.getVelocity()}function _setCallback(callback){this._callback=callback}function _wake(){this.PE.wake()}function _sleep(){this.PE.sleep()}function _update(){if(this.PE.isSleeping()){if(this._callback){var cb=this._callback;this._callback=undefined;cb()}return}if(_getEnergy.call(this)<this._absRestTolerance){_setParticlePosition.call(this,this.endState);_setParticleVelocity.call(this,[0,0,0]);_sleep.call(this)}}function _setupDefinition(definition){var defaults=SpringTransition.DEFAULT_OPTIONS;if(definition.period===undefined)definition.period=defaults.period;if(definition.dampingRatio===undefined)definition.dampingRatio=defaults.dampingRatio;if(definition.velocity===undefined)definition.velocity=defaults.velocity;if(definition.period<150){definition.period=150;console.warn("The period of a SpringTransition is capped at 150 ms. Use a SnapTransition for faster transitions")}this.spring.setOptions({period:definition.period,dampingRatio:definition.dampingRatio});_setParticleVelocity.call(this,definition.velocity)}function _setAbsoluteRestTolerance(){var distance=this.endState.sub(this.initState).normSquared();this._absRestTolerance=distance===0?this._restTolerance:this._restTolerance*distance}function _setTarget(target){this.endState.set(target);_setAbsoluteRestTolerance.call(this)}SpringTransition.prototype.reset=function reset(pos,vel){this._dimensions=pos instanceof Array?pos.length:0;this.initState.set(pos);_setParticlePosition.call(this,pos);_setTarget.call(this,pos);if(vel)_setParticleVelocity.call(this,vel);_setCallback.call(this,undefined)};SpringTransition.prototype.getVelocity=function getVelocity(){return _getParticleVelocity.call(this)};SpringTransition.prototype.setVelocity=function setVelocity(v){this.call(this,_setParticleVelocity(v))};SpringTransition.prototype.isActive=function isActive(){return!this.PE.isSleeping()};SpringTransition.prototype.halt=function halt(){this.set(this.get())};SpringTransition.prototype.get=function get(){_update.call(this);return _getParticlePosition.call(this)};SpringTransition.prototype.set=function set(endState,definition,callback){if(!definition){this.reset(endState);if(callback)callback();return}this._dimensions=endState instanceof Array?endState.length:0;_wake.call(this);_setupDefinition.call(this,definition);_setTarget.call(this,endState);_setCallback.call(this,callback)};module.exports=SpringTransition});define("famous/physics/constraints/Wall",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Wall(options){this.options=Object.create(Wall.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.diff=new Vector;this.impulse=new Vector;Constraint.call(this)}Wall.prototype=Object.create(Constraint.prototype);Wall.prototype.constructor=Wall;Wall.ON_CONTACT={REFLECT:0,SILENT:1};Wall.DEFAULT_OPTIONS={restitution:.5,drift:.5,slop:0,normal:[1,0,0],distance:0,onContact:Wall.ON_CONTACT.REFLECT};Wall.prototype.setOptions=function setOptions(options){if(options.normal!==undefined){if(options.normal instanceof Vector)this.options.normal=options.normal.clone();if(options.normal instanceof Array)this.options.normal=new Vector(options.normal)}if(options.restitution!==undefined)this.options.restitution=options.restitution;if(options.drift!==undefined)this.options.drift=options.drift;if(options.slop!==undefined)this.options.slop=options.slop;if(options.distance!==undefined)this.options.distance=options.distance;if(options.onContact!==undefined)this.options.onContact=options.onContact};function _getNormalVelocity(n,v){return v.dot(n)}function _getDistanceFromOrigin(p){var n=this.options.normal;var d=this.options.distance;return p.dot(n)+d}function _onEnter(particle,overlap,dt){var p=particle.position;var v=particle.velocity;var m=particle.mass;var n=this.options.normal;var action=this.options.onContact;var restitution=this.options.restitution;var impulse=this.impulse;var drift=this.options.drift;var slop=-this.options.slop;var gamma=0;if(this._eventOutput){var data={particle:particle,wall:this,overlap:overlap,normal:n};this._eventOutput.emit("preCollision",data);this._eventOutput.emit("collision",data)}switch(action){case Wall.ON_CONTACT.REFLECT:var lambda=overlap<slop?-((1+restitution)*n.dot(v)+drift/dt*(overlap-slop))/(m*dt+gamma):-((1+restitution)*n.dot(v))/(m*dt+gamma);impulse.set(n.mult(dt*lambda));particle.applyImpulse(impulse);particle.setPosition(p.add(n.mult(-overlap)));break}if(this._eventOutput)this._eventOutput.emit("postCollision",data)}function _onExit(particle,overlap,dt){var action=this.options.onContact;var p=particle.position;var n=this.options.normal;if(action===Wall.ON_CONTACT.REFLECT){particle.setPosition(p.add(n.mult(-overlap)))}}Wall.prototype.applyConstraint=function applyConstraint(targets,source,dt){var n=this.options.normal;for(var i=0;i<targets.length;i++){var particle=targets[i];var p=particle.position;var v=particle.velocity;var r=particle.radius||0;var overlap=_getDistanceFromOrigin.call(this,p.add(n.mult(-r)));var nv=_getNormalVelocity.call(this,n,v);if(overlap<=0){if(nv<0)_onEnter.call(this,particle,overlap,dt);else _onExit.call(this,particle,overlap,dt)}}};module.exports=Wall});define("famous/transitions/WallTransition",["require","exports","module","famous/physics/PhysicsEngine","famous/physics/bodies/Particle","famous/physics/forces/Spring","famous/physics/constraints/Wall","famous/math/Vector"],function(require,exports,module){var PE=require("famous/physics/PhysicsEngine");var Particle=require("famous/physics/bodies/Particle");var Spring=require("famous/physics/forces/Spring");var Wall=require("famous/physics/constraints/Wall");var Vector=require("famous/math/Vector");function WallTransition(state){state=state||0;this.endState=new Vector(state);this.initState=new Vector;this.spring=new Spring({anchor:this.endState});this.wall=new Wall;this._restTolerance=1e-10;this._dimensions=1;this._absRestTolerance=this._restTolerance;this._callback=undefined;this.PE=new PE;this.particle=new Particle;this.PE.addBody(this.particle);this.PE.attach([this.wall,this.spring],this.particle)}WallTransition.SUPPORTS_MULTIPLE=3;WallTransition.DEFAULT_OPTIONS={period:300,dampingRatio:.5,velocity:0,resitution:.5};function _getEnergy(){return this.particle.getEnergy()+this.spring.getEnergy(this.particle)}function _setAbsoluteRestTolerance(){var distance=this.endState.sub(this.initState).normSquared();this._absRestTolerance=distance===0?this._restTolerance:this._restTolerance*distance}function _wake(){this.PE.wake()}function _sleep(){this.PE.sleep()}function _setTarget(target){this.endState.set(target);var dist=this.endState.sub(this.initState).norm();this.wall.setOptions({distance:this.endState.norm(),normal:dist===0?this.particle.velocity.normalize(-1):this.endState.sub(this.initState).normalize(-1)});_setAbsoluteRestTolerance.call(this)}function _setParticlePosition(p){this.particle.position.set(p)}function _setParticleVelocity(v){this.particle.velocity.set(v)}function _getParticlePosition(){return this._dimensions===0?this.particle.getPosition1D():this.particle.getPosition()}function _getParticleVelocity(){return this._dimensions===0?this.particle.getVelocity1D():this.particle.getVelocity()}function _setCallback(callback){this._callback=callback}function _update(){if(this.PE.isSleeping()){if(this._callback){var cb=this._callback;this._callback=undefined;cb()}return}var energy=_getEnergy.call(this);if(energy<this._absRestTolerance){_sleep.call(this);_setParticlePosition.call(this,this.endState);_setParticleVelocity.call(this,[0,0,0])}}function _setupDefinition(def){var defaults=WallTransition.DEFAULT_OPTIONS;if(def.period===undefined)def.period=defaults.period;if(def.dampingRatio===undefined)def.dampingRatio=defaults.dampingRatio;if(def.velocity===undefined)def.velocity=defaults.velocity;if(def.restitution===undefined)def.restitution=defaults.restitution;this.spring.setOptions({period:def.period,dampingRatio:def.dampingRatio});this.wall.setOptions({restitution:def.restitution});_setParticleVelocity.call(this,def.velocity)}WallTransition.prototype.reset=function reset(state,velocity){this._dimensions=state instanceof Array?state.length:0;this.initState.set(state);_setParticlePosition.call(this,state);if(velocity)_setParticleVelocity.call(this,velocity);_setTarget.call(this,state);_setCallback.call(this,undefined)};WallTransition.prototype.getVelocity=function getVelocity(){return _getParticleVelocity.call(this)};WallTransition.prototype.setVelocity=function setVelocity(velocity){this.call(this,_setParticleVelocity(velocity))};WallTransition.prototype.isActive=function isActive(){return!this.PE.isSleeping()};WallTransition.prototype.halt=function halt(){this.set(this.get())};WallTransition.prototype.get=function get(){_update.call(this);return _getParticlePosition.call(this)};WallTransition.prototype.set=function set(state,definition,callback){if(!definition){this.reset(state);if(callback)callback();return}this._dimensions=state instanceof Array?state.length:0;_wake.call(this);_setupDefinition.call(this,definition);_setTarget.call(this,state);_setCallback.call(this,callback)};module.exports=WallTransition});define("famous/surfaces/CanvasSurface",["require","exports","module","famous/core/Surface"],function(require,exports,module){var Surface=require("famous/core/Surface");function CanvasSurface(options){if(options&&options.canvasSize)this._canvasSize=options.canvasSize;Surface.apply(this,arguments);if(!this._canvasSize)this._canvasSize=this.getSize();this._backBuffer=document.createElement("canvas");if(this._canvasSize){this._backBuffer.width=this._canvasSize[0];this._backBuffer.height=this._canvasSize[1]}this._contextId=undefined}CanvasSurface.prototype=Object.create(Surface.prototype);CanvasSurface.prototype.constructor=CanvasSurface;CanvasSurface.prototype.elementType="canvas";CanvasSurface.prototype.elementClass="famous-surface";CanvasSurface.prototype.setContent=function setContent(){};CanvasSurface.prototype.deploy=function deploy(target){if(this._canvasSize){target.width=this._canvasSize[0];target.height=this._canvasSize[1]}if(this._contextId==="2d"){target.getContext(this._contextId).drawImage(this._backBuffer,0,0);this._backBuffer.width=0;this._backBuffer.height=0}};CanvasSurface.prototype.recall=function recall(target){var size=this.getSize();this._backBuffer.width=target.width;this._backBuffer.height=target.height;if(this._contextId==="2d"){this._backBuffer.getContext(this._contextId).drawImage(target,0,0);target.width=0;target.height=0}};CanvasSurface.prototype.getContext=function getContext(contextId){this._contextId=contextId;return this._currTarget?this._currTarget.getContext(contextId):this._backBuffer.getContext(contextId)};CanvasSurface.prototype.setSize=function setSize(size,canvasSize){Surface.prototype.setSize.apply(this,arguments);if(canvasSize)this._canvasSize=[canvasSize[0],canvasSize[1]];if(this._currTarget){this._currTarget.width=this._canvasSize[0];this._currTarget.height=this._canvasSize[1]}};module.exports=CanvasSurface});define("famous/surfaces/ContainerSurface",["require","exports","module","famous/core/Surface","famous/core/Context"],function(require,exports,module){var Surface=require("famous/core/Surface");var Context=require("famous/core/Context");function ContainerSurface(options){Surface.call(this,options);this._container=document.createElement("div");this._container.classList.add("famous-group");this._container.classList.add("famous-container-group");this._shouldRecalculateSize=false;this.context=new Context(this._container);this.setContent(this._container)}ContainerSurface.prototype=Object.create(Surface.prototype);ContainerSurface.prototype.constructor=ContainerSurface;ContainerSurface.prototype.elementType="div";ContainerSurface.prototype.elementClass="famous-surface";ContainerSurface.prototype.add=function add(){return this.context.add.apply(this.context,arguments)};ContainerSurface.prototype.render=function render(){if(this._sizeDirty)this._shouldRecalculateSize=true;return Surface.prototype.render.apply(this,arguments)};ContainerSurface.prototype.deploy=function deploy(){this._shouldRecalculateSize=true;return Surface.prototype.deploy.apply(this,arguments)};ContainerSurface.prototype.commit=function commit(context,transform,opacity,origin,size){var previousSize=this._size?[this._size[0],this._size[1]]:null;var result=Surface.prototype.commit.apply(this,arguments);if(this._shouldRecalculateSize||previousSize&&(this._size[0]!==previousSize[0]||this._size[1]!==previousSize[1])){this.context.setSize();this._shouldRecalculateSize=false}this.context.update();return result};module.exports=ContainerSurface});define("famous/surfaces/FormContainerSurface",["require","exports","module","./ContainerSurface"],function(require,exports,module){var ContainerSurface=require("./ContainerSurface");function FormContainerSurface(options){if(options)this._method=options.method||"";ContainerSurface.apply(this,arguments)}FormContainerSurface.prototype=Object.create(ContainerSurface.prototype);FormContainerSurface.prototype.constructor=FormContainerSurface;FormContainerSurface.prototype.elementType="form";FormContainerSurface.prototype.deploy=function deploy(target){if(this._method)target.method=this._method;return ContainerSurface.prototype.deploy.apply(this,arguments)};module.exports=FormContainerSurface});define("famous/surfaces/ImageSurface",["require","exports","module","famous/core/Surface"],function(require,exports,module){var Surface=require("famous/core/Surface");function ImageSurface(options){this._imageUrl=undefined;Surface.apply(this,arguments)}ImageSurface.prototype=Object.create(Surface.prototype);ImageSurface.prototype.constructor=ImageSurface;ImageSurface.prototype.elementType="img";ImageSurface.prototype.elementClass="famous-surface";ImageSurface.prototype.setContent=function setContent(imageUrl){this._imageUrl=imageUrl;this._contentDirty=true};ImageSurface.prototype.deploy=function deploy(target){target.src=this._imageUrl||""};ImageSurface.prototype.recall=function recall(target){target.src=""};module.exports=ImageSurface});define("famous/surfaces/InputSurface",["require","exports","module","famous/core/Surface"],function(require,exports,module){var Surface=require("famous/core/Surface");function InputSurface(options){this._placeholder=options.placeholder||"";this._value=options.value||"";this._type=options.type||"text";this._name=options.name||"";Surface.apply(this,arguments);this.on("click",this.focus.bind(this));window.addEventListener("click",function(event){if(event.target!==this._currTarget)this.blur()}.bind(this))}InputSurface.prototype=Object.create(Surface.prototype);InputSurface.prototype.constructor=InputSurface;InputSurface.prototype.elementType="input";InputSurface.prototype.elementClass="famous-surface";InputSurface.prototype.setPlaceholder=function setPlaceholder(str){this._placeholder=str;this._contentDirty=true;return this};InputSurface.prototype.focus=function focus(){if(this._currTarget)this._currTarget.focus();return this};InputSurface.prototype.blur=function blur(){if(this._currTarget)this._currTarget.blur();return this};InputSurface.prototype.setValue=function setValue(str){this._value=str;this._contentDirty=true;return this};InputSurface.prototype.setType=function setType(str){this._type=str;this._contentDirty=true;return this};InputSurface.prototype.getValue=function getValue(){if(this._currTarget){return this._currTarget.value}else{return this._value}};InputSurface.prototype.setName=function setName(str){this._name=str;this._contentDirty=true;return this};InputSurface.prototype.getName=function getName(){return this._name};InputSurface.prototype.deploy=function deploy(target){if(this._placeholder!=="")target.placeholder=this._placeholder;target.value=this._value;target.type=this._type;target.name=this._name};module.exports=InputSurface});define("famous/surfaces/SubmitInputSurface",["require","exports","module","./InputSurface"],function(require,exports,module){var InputSurface=require("./InputSurface");function SubmitInputSurface(options){InputSurface.apply(this,arguments);this._type="submit";if(options&&options.onClick)this.setOnClick(options.onClick)}SubmitInputSurface.prototype=Object.create(InputSurface.prototype);SubmitInputSurface.prototype.constructor=SubmitInputSurface;SubmitInputSurface.prototype.setOnClick=function(onClick){this.onClick=onClick};SubmitInputSurface.prototype.deploy=function deploy(target){if(this.onclick)target.onClick=this.onClick;InputSurface.prototype.deploy.apply(this,arguments)};module.exports=SubmitInputSurface});define("famous/surfaces/TextareaSurface",["require","exports","module","famous/core/Surface"],function(require,exports,module){var Surface=require("famous/core/Surface");function TextareaSurface(options){this._placeholder=options.placeholder||"";this._value=options.value||"";this._name=options.name||"";this._wrap=options.wrap||"";this._cols=options.cols||"";this._rows=options.rows||"";Surface.apply(this,arguments);this.on("click",this.focus.bind(this))}TextareaSurface.prototype=Object.create(Surface.prototype);TextareaSurface.prototype.constructor=TextareaSurface;TextareaSurface.prototype.elementType="textarea";TextareaSurface.prototype.elementClass="famous-surface";TextareaSurface.prototype.setPlaceholder=function setPlaceholder(str){this._placeholder=str;this._contentDirty=true;return this};TextareaSurface.prototype.focus=function focus(){if(this._currTarget)this._currTarget.focus();return this};TextareaSurface.prototype.blur=function blur(){if(this._currTarget)this._currTarget.blur();return this};TextareaSurface.prototype.setValue=function setValue(str){this._value=str;this._contentDirty=true;return this};TextareaSurface.prototype.getValue=function getValue(){if(this._currTarget){return this._currTarget.value}else{return this._value}};TextareaSurface.prototype.setName=function setName(str){this._name=str;this._contentDirty=true;return this};TextareaSurface.prototype.getName=function getName(){return this._name};TextareaSurface.prototype.setWrap=function setWrap(str){this._wrap=str;this._contentDirty=true;return this};TextareaSurface.prototype.setColumns=function setColumns(num){this._cols=num;this._contentDirty=true;return this};TextareaSurface.prototype.setRows=function setRows(num){this._rows=num;this._contentDirty=true;return this};TextareaSurface.prototype.deploy=function deploy(target){if(this._placeholder!=="")target.placeholder=this._placeholder;if(this._value!=="")target.value=this._value;if(this._name!=="")target.name=this._name;if(this._wrap!=="")target.wrap=this._wrap;if(this._cols!=="")target.cols=this._cols;if(this._rows!=="")target.rows=this._rows};module.exports=TextareaSurface});define("famous/surfaces/VideoSurface",["require","exports","module","famous/core/Surface"],function(require,exports,module){var Surface=require("famous/core/Surface");function VideoSurface(options){this._videoUrl=undefined;this.options=Object.create(VideoSurface.DEFAULT_OPTIONS);if(options)this.setOptions(options);Surface.apply(this,arguments)}VideoSurface.prototype=Object.create(Surface.prototype);VideoSurface.prototype.constructor=VideoSurface;VideoSurface.DEFAULT_OPTIONS={autoplay:false};VideoSurface.prototype.elementType="video";VideoSurface.prototype.elementClass="famous-surface";VideoSurface.prototype.setOptions=function setOptions(options){for(var key in VideoSurface.DEFAULT_OPTIONS){if(options[key]!==undefined)this.options[key]=options[key]}};VideoSurface.prototype.setContent=function setContent(videoUrl){this._videoUrl=videoUrl;this._contentDirty=true};VideoSurface.prototype.deploy=function deploy(target){target.src=this._videoUrl;target.autoplay=this.options.autoplay};VideoSurface.prototype.recall=function recall(target){target.src=""};module.exports=VideoSurface});define("famous/utilities/KeyCodes",["require","exports","module"],function(require,exports,module){var KeyCodes={0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,a:97,b:98,c:99,d:100,e:101,f:102,g:103,h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114,s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,ENTER:13,LEFT_ARROW:37,RIGHT_ARROW:39,UP_ARROW:38,DOWN_ARROW:40,SPACE:32,SHIFT:16,TAB:9};module.exports=KeyCodes});define("famous/utilities/Timer",["require","exports","module","famous/core/Engine"],function(require,exports,module){var FamousEngine=require("famous/core/Engine");var _event="prerender";var getTime=window.performance?function(){return window.performance.now()}:function(){return Date.now()};function addTimerFunction(fn){FamousEngine.on(_event,fn);return fn}function setTimeout(fn,duration){var t=getTime();var callback=function(){var t2=getTime();if(t2-t>=duration){fn.apply(this,arguments);FamousEngine.removeListener(_event,callback)}};return addTimerFunction(callback)}function setInterval(fn,duration){var t=getTime();var callback=function(){var t2=getTime();if(t2-t>=duration){fn.apply(this,arguments);t=getTime()}};return addTimerFunction(callback)}function after(fn,numTicks){if(numTicks===undefined)return undefined;var callback=function(){numTicks--;if(numTicks<=0){fn.apply(this,arguments);clear(callback)}};return addTimerFunction(callback)}function every(fn,numTicks){numTicks=numTicks||1;var initial=numTicks;var callback=function(){numTicks--;if(numTicks<=0){fn.apply(this,arguments);numTicks=initial}};return addTimerFunction(callback)}function clear(fn){FamousEngine.removeListener(_event,fn)}function debounce(func,wait){var timeout;var ctx;var timestamp;var result;var args;return function(){ctx=this;args=arguments;timestamp=getTime();var fn=function(){var last=getTime-timestamp;if(last<wait){timeout=setTimeout(fn,wait-last)}else{timeout=null;result=func.apply(ctx,args)}};clear(timeout);timeout=setTimeout(fn,wait);return result}}module.exports={setTimeout:setTimeout,setInterval:setInterval,debounce:debounce,after:after,every:every,clear:clear}});define("famous/views/ContextualView",["require","exports","module","famous/core/Entity","famous/core/Transform","famous/core/EventHandler","famous/core/OptionsManager"],function(require,exports,module){var Entity=require("famous/core/Entity");var Transform=require("famous/core/Transform");var EventHandler=require("famous/core/EventHandler");var OptionsManager=require("famous/core/OptionsManager");function ContextualView(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS||ContextualView.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this._id=Entity.register(this)}ContextualView.DEFAULT_OPTIONS={};ContextualView.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};ContextualView.prototype.getOptions=function getOptions(){return this._optionsManager.getOptions()};ContextualView.prototype.render=function render(){return this._id};ContextualView.prototype.commit=function commit(context){};module.exports=ContextualView});define("famous/views/SequentialLayout",["require","exports","module","famous/core/OptionsManager","famous/core/Transform","famous/core/ViewSequence","famous/utilities/Utility"],function(require,exports,module){var OptionsManager=require("famous/core/OptionsManager");var Transform=require("famous/core/Transform");var ViewSequence=require("famous/core/ViewSequence");var Utility=require("famous/utilities/Utility");function SequentialLayout(options){this._items=null;this._size=null;this._outputFunction=SequentialLayout.DEFAULT_OUTPUT_FUNCTION;this.options=Object.create(this.constructor.DEFAULT_OPTIONS);this.optionsManager=new OptionsManager(this.options);this._itemsCache=[];this._outputCache={size:null,target:this._itemsCache};if(options)this.setOptions(options)}SequentialLayout.DEFAULT_OPTIONS={direction:Utility.Direction.Y,itemSpacing:0,defaultItemSize:[50,50]};SequentialLayout.DEFAULT_OUTPUT_FUNCTION=function DEFAULT_OUTPUT_FUNCTION(input,offset,index){var transform=this.options.direction===Utility.Direction.X?Transform.translate(offset,0):Transform.translate(0,offset);return{transform:transform,target:input.render()}};SequentialLayout.prototype.getSize=function getSize(){if(!this._size)this.render();return this._size};SequentialLayout.prototype.sequenceFrom=function sequenceFrom(items){if(items instanceof Array)items=new ViewSequence(items);this._items=items;return this};SequentialLayout.prototype.setOptions=function setOptions(options){this.optionsManager.setOptions.apply(this.optionsManager,arguments);return this};SequentialLayout.prototype.setOutputFunction=function setOutputFunction(outputFunction){this._outputFunction=outputFunction;return this};SequentialLayout.prototype.render=function render(){var length=0;var girth=0;var lengthDim=this.options.direction===Utility.Direction.X?0:1;var girthDim=this.options.direction===Utility.Direction.X?1:0;var currentNode=this._items;var result=this._itemsCache;var i=0;while(currentNode){var item=currentNode.get();if(!item)break;var itemSize;if(item&&item.getSize)itemSize=item.getSize();if(!itemSize)itemSize=this.options.defaultItemSize;if(itemSize[girthDim]!==true)girth=Math.max(girth,itemSize[girthDim]);var output=this._outputFunction.call(this,item,length,i);result[i]=output;if(itemSize[lengthDim]&&itemSize[lengthDim]!==true)length+=itemSize[lengthDim]+this.options.itemSpacing;currentNode=currentNode.getNext();i++}this._itemsCache.splice(i);if(!girth)girth=undefined;if(!this._size)this._size=[0,0];this._size[lengthDim]=length-this.options.itemSpacing;this._size[girthDim]=girth;this._outputCache.size=this.getSize();return this._outputCache};module.exports=SequentialLayout});define("famous/views/Deck",["require","exports","module","famous/core/Transform","famous/core/OptionsManager","famous/transitions/Transitionable","famous/utilities/Utility","./SequentialLayout"],function(require,exports,module){var Transform=require("famous/core/Transform");var OptionsManager=require("famous/core/OptionsManager");var Transitionable=require("famous/transitions/Transitionable");var Utility=require("famous/utilities/Utility");var SequentialLayout=require("./SequentialLayout");function Deck(options){SequentialLayout.apply(this,arguments);this.state=new Transitionable(0);this._isOpen=false;this.setOutputFunction(function(input,offset,index){var state=_getState.call(this);var positionMatrix=this.options.direction===Utility.Direction.X?Transform.translate(state*offset,0,.001*(state-1)*offset):Transform.translate(0,state*offset,.001*(state-1)*offset);var output=input.render();if(this.options.stackRotation){var amount=this.options.stackRotation*index*(1-state);output={transform:Transform.rotateZ(amount),origin:[.5,.5],target:output}}return{transform:positionMatrix,size:input.getSize(),target:output}})}Deck.prototype=Object.create(SequentialLayout.prototype);Deck.prototype.constructor=Deck;Deck.DEFAULT_OPTIONS=OptionsManager.patch(SequentialLayout.DEFAULT_OPTIONS,{transition:{curve:"easeOutBounce",duration:500},stackRotation:0});Deck.prototype.getSize=function getSize(){var originalSize=SequentialLayout.prototype.getSize.apply(this,arguments);var firstSize=this._items?this._items.get().getSize():[0,0];if(!firstSize)firstSize=[0,0];var state=_getState.call(this);var invState=1-state;return[firstSize[0]*invState+originalSize[0]*state,firstSize[1]*invState+originalSize[1]*state]};function _getState(returnFinal){if(returnFinal)return this._isOpen?1:0;else return this.state.get()}function _setState(pos,transition,callback){this.state.halt();this.state.set(pos,transition,callback)}Deck.prototype.isOpen=function isOpen(){return this._isOpen};Deck.prototype.open=function open(callback){this._isOpen=true;_setState.call(this,1,this.options.transition,callback)};Deck.prototype.close=function close(callback){this._isOpen=false;_setState.call(this,0,this.options.transition,callback)};Deck.prototype.toggle=function toggle(callback){if(this._isOpen)this.close(callback);else this.open(callback)};module.exports=Deck});define("famous/views/RenderController",["require","exports","module","famous/core/Modifier","famous/core/RenderNode","famous/core/Transform","famous/transitions/Transitionable","famous/core/View"],function(require,exports,module){var Modifier=require("famous/core/Modifier");var RenderNode=require("famous/core/RenderNode");var Transform=require("famous/core/Transform");var Transitionable=require("famous/transitions/Transitionable");var View=require("famous/core/View");function RenderController(options){View.apply(this,arguments);this._showing=-1;this._outgoingRenderables=[];this._nextRenderable=null;this._renderables=[];this._nodes=[];this._modifiers=[];this._states=[];this.inTransformMap=RenderController.DefaultMap.transform;this.inOpacityMap=RenderController.DefaultMap.opacity;this.inOriginMap=RenderController.DefaultMap.origin;this.outTransformMap=RenderController.DefaultMap.transform;this.outOpacityMap=RenderController.DefaultMap.opacity;this.outOriginMap=RenderController.DefaultMap.origin;this._output=[]}RenderController.prototype=Object.create(View.prototype);RenderController.prototype.constructor=RenderController;RenderController.DEFAULT_OPTIONS={inTransition:true,outTransition:true,overlap:true};RenderController.DefaultMap={transform:function(){return Transform.identity},opacity:function(progress){return progress},origin:null};function _mappedState(map,state){return map(state.get())}RenderController.prototype.inTransformFrom=function inTransformFrom(transform){if(transform instanceof Function)this.inTransformMap=transform;else if(transform&&transform.get)this.inTransformMap=transform.get.bind(transform);else throw new Error("inTransformFrom takes only function or getter object"); | |
return this};RenderController.prototype.inOpacityFrom=function inOpacityFrom(opacity){if(opacity instanceof Function)this.inOpacityMap=opacity;else if(opacity&&opacity.get)this.inOpacityMap=opacity.get.bind(opacity);else throw new Error("inOpacityFrom takes only function or getter object");return this};RenderController.prototype.inOriginFrom=function inOriginFrom(origin){if(origin instanceof Function)this.inOriginMap=origin;else if(origin&&origin.get)this.inOriginMap=origin.get.bind(origin);else throw new Error("inOriginFrom takes only function or getter object");return this};RenderController.prototype.outTransformFrom=function outTransformFrom(transform){if(transform instanceof Function)this.outTransformMap=transform;else if(transform&&transform.get)this.outTransformMap=transform.get.bind(transform);else throw new Error("inTransformFrom takes only function or getter object");return this};RenderController.prototype.outOpacityFrom=function outOpacityFrom(opacity){if(opacity instanceof Function)this.outOpacityMap=opacity;else if(opacity&&opacity.get)this.outOpacityMap=opacity.get.bind(opacity);else throw new Error("inOpacityFrom takes only function or getter object");return this};RenderController.prototype.outOriginFrom=function outOriginFrom(origin){if(origin instanceof Function)this.outOriginMap=origin;else if(origin&&origin.get)this.outOriginMap=origin.get.bind(origin);else throw new Error("inOriginFrom takes only function or getter object");return this};RenderController.prototype.show=function show(renderable,transition,callback){if(!renderable){return this.hide(callback)}if(transition instanceof Function){callback=transition;transition=null}if(this._showing>=0){if(this.options.overlap)this.hide();else{if(this._nextRenderable){this._nextRenderable=renderable}else{this._nextRenderable=renderable;this.hide(function(){if(this._nextRenderable===renderable)this.show(this._nextRenderable,callback);this._nextRenderable=null})}return undefined}}var state=null;var renderableIndex=this._renderables.indexOf(renderable);if(renderableIndex>=0){this._showing=renderableIndex;state=this._states[renderableIndex];state.halt();var outgoingIndex=this._outgoingRenderables.indexOf(renderable);if(outgoingIndex>=0)this._outgoingRenderables.splice(outgoingIndex,1)}else{state=new Transitionable(0);var modifier=new Modifier({transform:this.inTransformMap?_mappedState.bind(this,this.inTransformMap,state):null,opacity:this.inOpacityMap?_mappedState.bind(this,this.inOpacityMap,state):null,origin:this.inOriginMap?_mappedState.bind(this,this.inOriginMap,state):null});var node=new RenderNode;node.add(modifier).add(renderable);this._showing=this._nodes.length;this._nodes.push(node);this._modifiers.push(modifier);this._states.push(state);this._renderables.push(renderable)}if(!transition)transition=this.options.inTransition;state.set(1,transition,callback)};RenderController.prototype.hide=function hide(transition,callback){if(this._showing<0)return;var index=this._showing;this._showing=-1;if(transition instanceof Function){callback=transition;transition=undefined}var node=this._nodes[index];var modifier=this._modifiers[index];var state=this._states[index];var renderable=this._renderables[index];modifier.transformFrom(this.outTransformMap?_mappedState.bind(this,this.outTransformMap,state):null);modifier.opacityFrom(this.outOpacityMap?_mappedState.bind(this,this.outOpacityMap,state):null);modifier.originFrom(this.outOriginMap?_mappedState.bind(this,this.outOriginMap,state):null);if(this._outgoingRenderables.indexOf(renderable)<0)this._outgoingRenderables.push(renderable);if(!transition)transition=this.options.outTransition;state.halt();state.set(0,transition,function(node,modifier,state,renderable){if(this._outgoingRenderables.indexOf(renderable)>=0){var index=this._nodes.indexOf(node);this._nodes.splice(index,1);this._modifiers.splice(index,1);this._states.splice(index,1);this._renderables.splice(index,1);this._outgoingRenderables.splice(this._outgoingRenderables.indexOf(renderable),1);if(this._showing>=index)this._showing--}if(callback)callback.call(this)}.bind(this,node,modifier,state,renderable))};RenderController.prototype.render=function render(){var result=this._output;if(result.length>this._nodes.length)result.splice(this._nodes.length);for(var i=0;i<this._nodes.length;i++){result[i]=this._nodes[i].render()}return result};module.exports=RenderController});define("famous/views/EdgeSwapper",["require","exports","module","famous/transitions/CachedMap","famous/core/Entity","famous/core/EventHandler","famous/core/Transform","./RenderController"],function(require,exports,module){var CachedMap=require("famous/transitions/CachedMap");var Entity=require("famous/core/Entity");var EventHandler=require("famous/core/EventHandler");var Transform=require("famous/core/Transform");var RenderController=require("./RenderController");function EdgeSwapper(options){this._currentTarget=null;this._size=[undefined,undefined];this._controller=new RenderController(options);this._controller.inTransformFrom(CachedMap.create(_transformMap.bind(this,1e-4)));this._controller.outTransformFrom(CachedMap.create(_transformMap.bind(this,-1e-4)));this._eventInput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);this._entityId=Entity.register(this);if(options)this.setOptions(options)}function _transformMap(zMax,progress){return Transform.translate(this._size[0]*(1-progress),0,zMax*(1-progress))}EdgeSwapper.prototype.show=function show(content){if(this._currentTarget)this._eventInput.unpipe(this._currentTarget);this._currentTarget=content;if(this._currentTarget&&this._currentTarget.trigger)this._eventInput.pipe(this._currentTarget);this._controller.show.apply(this._controller,arguments)};EdgeSwapper.prototype.setOptions=function setOptions(options){this._controller.setOptions(options)};EdgeSwapper.prototype.render=function render(){return this._entityId};EdgeSwapper.prototype.commit=function commit(context){this._size[0]=context.size[0];this._size[1]=context.size[1];return{transform:context.transform,opacity:context.opacity,origin:context.origin,size:context.size,target:this._controller.render()}};module.exports=EdgeSwapper});define("famous/views/FlexibleLayout",["require","exports","module","famous/core/Entity","famous/core/Transform","famous/core/OptionsManager","famous/core/EventHandler","famous/transitions/Transitionable"],function(require,exports,module){var Entity=require("famous/core/Entity");var Transform=require("famous/core/Transform");var OptionsManager=require("famous/core/OptionsManager");var EventHandler=require("famous/core/EventHandler");var Transitionable=require("famous/transitions/Transitionable");function FlexibleLayout(options){this.options=Object.create(FlexibleLayout.DEFAULT_OPTIONS);this.optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this.id=Entity.register(this);this._ratios=new Transitionable(this.options.ratios);this._nodes=[];this._cachedDirection=null;this._cachedTotalLength=false;this._cachedLengths=[];this._cachedTransforms=null;this._ratiosDirty=false;this._eventOutput=new EventHandler;EventHandler.setOutputHandler(this,this._eventOutput)}FlexibleLayout.DIRECTION_X=0;FlexibleLayout.DIRECTION_Y=1;FlexibleLayout.DEFAULT_OPTIONS={direction:FlexibleLayout.DIRECTION_X,transition:false,ratios:[]};function _reflow(ratios,length,direction){var currTransform;var translation=0;var flexLength=length;var ratioSum=0;var ratio;var node;var i;this._cachedLengths=[];this._cachedTransforms=[];for(i=0;i<ratios.length;i++){ratio=ratios[i];node=this._nodes[i];if(typeof ratio!=="number")flexLength-=node.getSize()[direction]||0;else ratioSum+=ratio}for(i=0;i<ratios.length;i++){node=this._nodes[i];ratio=ratios[i];length=typeof ratio==="number"?flexLength*ratio/ratioSum:node.getSize()[direction];currTransform=direction===FlexibleLayout.DIRECTION_X?Transform.translate(translation,0,0):Transform.translate(0,translation,0);this._cachedTransforms.push(currTransform);this._cachedLengths.push(length);translation+=length}}FlexibleLayout.prototype.render=function render(){return this.id};FlexibleLayout.prototype.setOptions=function setOptions(options){this.optionsManager.setOptions(options)};FlexibleLayout.prototype.sequenceFrom=function sequenceFrom(sequence){this._nodes=sequence;if(this._ratios.get().length===0){var ratios=[];for(var i=0;i<this._nodes.length;i++)ratios.push(1);this.setRatios(ratios)}};FlexibleLayout.prototype.setRatios=function setRatios(ratios,transition,callback){if(transition===undefined)transition=this.options.transition;var currRatios=this._ratios;if(currRatios.get().length===0)transition=undefined;if(currRatios.isActive())currRatios.halt();currRatios.set(ratios,transition,callback);this._ratiosDirty=true};FlexibleLayout.prototype.commit=function commit(context){var parentSize=context.size;var parentTransform=context.transform;var parentOrigin=context.origin;var ratios=this._ratios.get();var direction=this.options.direction;var length=parentSize[direction];var size;if(length!==this._cachedTotalLength||this._ratiosDirty||this._ratios.isActive()||direction!==this._cachedDirection){_reflow.call(this,ratios,length,direction);if(length!==this._cachedTotalLength)this._cachedTotalLength=length;if(direction!==this._cachedDirection)this._cachedDirection=direction;if(this._ratiosDirty)this._ratiosDirty=false}var result=[];for(var i=0;i<ratios.length;i++){size=[undefined,undefined];length=this._cachedLengths[i];size[direction]=length;result.push({transform:this._cachedTransforms[i],size:size,target:this._nodes[i].render()})}if(parentSize&&(parentOrigin[0]!==0&&parentOrigin[1]!==0))parentTransform=Transform.moveThen([-parentSize[0]*parentOrigin[0],-parentSize[1]*parentOrigin[1],0],parentTransform);return{transform:parentTransform,size:parentSize,target:result}};module.exports=FlexibleLayout});define("famous/views/Flipper",["require","exports","module","famous/core/Transform","famous/transitions/Transitionable","famous/core/RenderNode","famous/core/OptionsManager"],function(require,exports,module){var Transform=require("famous/core/Transform");var Transitionable=require("famous/transitions/Transitionable");var RenderNode=require("famous/core/RenderNode");var OptionsManager=require("famous/core/OptionsManager");function Flipper(options){this.options=Object.create(Flipper.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this.angle=new Transitionable(0);this.frontNode=undefined;this.backNode=undefined;this.flipped=false}Flipper.DIRECTION_X=0;Flipper.DIRECTION_Y=1;var SEPERATION_LENGTH=1;Flipper.DEFAULT_OPTIONS={transition:true,direction:Flipper.DIRECTION_X};Flipper.prototype.flip=function flip(transition,callback){var angle=this.flipped?0:Math.PI;this.setAngle(angle,transition,callback);this.flipped=!this.flipped};Flipper.prototype.setAngle=function setAngle(angle,transition,callback){if(transition===undefined)transition=this.options.transition;if(this.angle.isActive())this.angle.halt();this.angle.set(angle,transition,callback)};Flipper.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};Flipper.prototype.setFront=function setFront(node){this.frontNode=node};Flipper.prototype.setBack=function setBack(node){this.backNode=node};Flipper.prototype.render=function render(){var angle=this.angle.get();var frontTransform;var backTransform;if(this.options.direction===Flipper.DIRECTION_X){frontTransform=Transform.rotateY(angle);backTransform=Transform.rotateY(angle+Math.PI)}else{frontTransform=Transform.rotateX(angle);backTransform=Transform.rotateX(angle+Math.PI)}var result=[];if(this.frontNode){result.push({transform:frontTransform,target:this.frontNode.render()})}if(this.backNode){result.push({transform:Transform.moveThen([0,0,SEPERATION_LENGTH],backTransform),target:this.backNode.render()})}return result};module.exports=Flipper});define("famous/views/GridLayout",["require","exports","module","famous/core/Entity","famous/core/RenderNode","famous/core/Transform","famous/core/ViewSequence","famous/core/EventHandler","famous/core/Modifier","famous/core/OptionsManager","famous/transitions/Transitionable","famous/transitions/TransitionableTransform"],function(require,exports,module){var Entity=require("famous/core/Entity");var RenderNode=require("famous/core/RenderNode");var Transform=require("famous/core/Transform");var ViewSequence=require("famous/core/ViewSequence");var EventHandler=require("famous/core/EventHandler");var Modifier=require("famous/core/Modifier");var OptionsManager=require("famous/core/OptionsManager");var Transitionable=require("famous/transitions/Transitionable");var TransitionableTransform=require("famous/transitions/TransitionableTransform");function GridLayout(options){this.options=Object.create(GridLayout.DEFAULT_OPTIONS);this.optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this.id=Entity.register(this);this._modifiers=[];this._states=[];this._contextSizeCache=[0,0];this._dimensionsCache=[0,0];this._activeCount=0;this._eventOutput=new EventHandler;EventHandler.setOutputHandler(this,this._eventOutput)}function _reflow(size,cols,rows){var usableSize=[size[0],size[1]];usableSize[0]-=this.options.gutterSize[0]*(cols-1);usableSize[1]-=this.options.gutterSize[1]*(rows-1);var rowSize=Math.round(usableSize[1]/rows);var colSize=Math.round(usableSize[0]/cols);var currY=0;var currX;var currIndex=0;for(var i=0;i<rows;i++){currX=0;for(var j=0;j<cols;j++){if(this._modifiers[currIndex]===undefined){_createModifier.call(this,currIndex,[colSize,rowSize],[currX,currY,0],1)}else{_animateModifier.call(this,currIndex,[colSize,rowSize],[currX,currY,0],1)}currIndex++;currX+=colSize+this.options.gutterSize[0]}currY+=rowSize+this.options.gutterSize[1]}this._dimensionsCache=[this.options.dimensions[0],this.options.dimensions[1]];this._contextSizeCache=[size[0],size[1]];this._activeCount=rows*cols;for(i=this._activeCount;i<this._modifiers.length;i++)_animateModifier.call(this,i,[Math.round(colSize),Math.round(rowSize)],[0,0],0);this._eventOutput.emit("reflow")}function _createModifier(index,size,position,opacity){var transitionItem={transform:new TransitionableTransform(Transform.translate.apply(null,position)),opacity:new Transitionable(opacity),size:new Transitionable(size)};var modifier=new Modifier({transform:transitionItem.transform,opacity:transitionItem.opacity,size:transitionItem.size});this._states[index]=transitionItem;this._modifiers[index]=modifier}function _animateModifier(index,size,position,opacity){var currState=this._states[index];var currSize=currState.size;var currOpacity=currState.opacity;var currTransform=currState.transform;var transition=this.options.transition;currTransform.halt();currOpacity.halt();currSize.halt();currTransform.setTranslate(position,transition);currSize.set(size,transition);currOpacity.set(opacity,transition)}GridLayout.DEFAULT_OPTIONS={dimensions:[1,1],transition:false,gutterSize:[0,0]};GridLayout.prototype.render=function render(){return this.id};GridLayout.prototype.setOptions=function setOptions(options){return this.optionsManager.setOptions(options)};GridLayout.prototype.sequenceFrom=function sequenceFrom(sequence){if(sequence instanceof Array)sequence=new ViewSequence(sequence);this.sequence=sequence};GridLayout.prototype.commit=function commit(context){var transform=context.transform;var opacity=context.opacity;var origin=context.origin;var size=context.size;var cols=this.options.dimensions[0];var rows=this.options.dimensions[1];if(size[0]!==this._contextSizeCache[0]||size[1]!==this._contextSizeCache[1]||cols!==this._dimensionsCache[0]||rows!==this._dimensionsCache[1]){_reflow.call(this,size,cols,rows)}var sequence=this.sequence;var result=[];var currIndex=0;while(sequence&&currIndex<this._modifiers.length){var item=sequence.get();var modifier=this._modifiers[currIndex];if(currIndex>=this._activeCount&&this._states[currIndex].opacity.isActive()){this._modifiers.splice(currIndex,1);this._states.splice(currIndex,1)}if(item){result.push(modifier.modify({origin:origin,target:item.render()}))}sequence=sequence.getNext();currIndex++}if(size)transform=Transform.moveThen([-size[0]*origin[0],-size[1]*origin[1],0],transform);return{transform:transform,opacity:opacity,size:size,target:result}};module.exports=GridLayout});define("famous/views/HeaderFooterLayout",["require","exports","module","famous/core/Entity","famous/core/RenderNode","famous/core/Transform","famous/core/OptionsManager"],function(require,exports,module){var Entity=require("famous/core/Entity");var RenderNode=require("famous/core/RenderNode");var Transform=require("famous/core/Transform");var OptionsManager=require("famous/core/OptionsManager");function HeaderFooterLayout(options){this.options=Object.create(HeaderFooterLayout.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this._entityId=Entity.register(this);this.header=new RenderNode;this.footer=new RenderNode;this.content=new RenderNode}HeaderFooterLayout.DIRECTION_X=0;HeaderFooterLayout.DIRECTION_Y=1;HeaderFooterLayout.DEFAULT_OPTIONS={direction:HeaderFooterLayout.DIRECTION_Y,headerSize:undefined,footerSize:undefined,defaultHeaderSize:0,defaultFooterSize:0};HeaderFooterLayout.prototype.render=function render(){return this._entityId};HeaderFooterLayout.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};function _resolveNodeSize(node,defaultSize){var nodeSize=node.getSize();return nodeSize?nodeSize[this.options.direction]:defaultSize}function _outputTransform(offset){if(this.options.direction===HeaderFooterLayout.DIRECTION_X)return Transform.translate(offset,0,0);else return Transform.translate(0,offset,0)}function _finalSize(directionSize,size){if(this.options.direction===HeaderFooterLayout.DIRECTION_X)return[directionSize,size[1]];else return[size[0],directionSize]}HeaderFooterLayout.prototype.commit=function commit(context){var transform=context.transform;var origin=context.origin;var size=context.size;var opacity=context.opacity;var headerSize=this.options.headerSize!==undefined?this.options.headerSize:_resolveNodeSize.call(this,this.header,this.options.defaultHeaderSize);var footerSize=this.options.footerSize!==undefined?this.options.footerSize:_resolveNodeSize.call(this,this.footer,this.options.defaultFooterSize);var contentSize=size[this.options.direction]-headerSize-footerSize;if(size)transform=Transform.moveThen([-size[0]*origin[0],-size[1]*origin[1],0],transform);var result=[{size:_finalSize.call(this,headerSize,size),target:this.header.render()},{transform:_outputTransform.call(this,headerSize),size:_finalSize.call(this,contentSize,size),target:this.content.render()},{transform:_outputTransform.call(this,headerSize+contentSize),size:_finalSize.call(this,footerSize,size),target:this.footer.render()}];return{transform:transform,opacity:opacity,size:size,target:result}};module.exports=HeaderFooterLayout});define("famous/views/Lightbox",["require","exports","module","famous/core/Transform","famous/core/Modifier","famous/core/RenderNode","famous/utilities/Utility","famous/core/OptionsManager","famous/transitions/Transitionable","famous/transitions/TransitionableTransform"],function(require,exports,module){var Transform=require("famous/core/Transform");var Modifier=require("famous/core/Modifier");var RenderNode=require("famous/core/RenderNode");var Utility=require("famous/utilities/Utility");var OptionsManager=require("famous/core/OptionsManager");var Transitionable=require("famous/transitions/Transitionable");var TransitionableTransform=require("famous/transitions/TransitionableTransform");function Lightbox(options){this.options=Object.create(Lightbox.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this._showing=false;this.nodes=[];this.transforms=[];this.states=[]}Lightbox.DEFAULT_OPTIONS={inTransform:Transform.scale(.001,.001,.001),inOpacity:0,inOrigin:[.5,.5],outTransform:Transform.scale(.001,.001,.001),outOpacity:0,outOrigin:[.5,.5],showTransform:Transform.identity,showOpacity:1,showOrigin:[.5,.5],inTransition:true,outTransition:true,overlap:false};Lightbox.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};Lightbox.prototype.show=function show(renderable,transition,callback){if(!renderable){return this.hide(callback)}if(transition instanceof Function){callback=transition;transition=undefined}if(this._showing){if(this.options.overlap)this.hide();else{return this.hide(this.show.bind(this,renderable,transition,callback))}}this._showing=true;var stateItem={transform:new TransitionableTransform(this.options.inTransform),origin:new Transitionable(this.options.inOrigin),opacity:new Transitionable(this.options.inOpacity)};var transform=new Modifier({transform:stateItem.transform,opacity:stateItem.opacity,origin:stateItem.origin});var node=new RenderNode;node.add(transform).add(renderable);this.nodes.push(node);this.states.push(stateItem);this.transforms.push(transform);var _cb=callback?Utility.after(3,callback):undefined;if(!transition)transition=this.options.inTransition;stateItem.transform.set(this.options.showTransform,transition,_cb);stateItem.opacity.set(this.options.showOpacity,transition,_cb);stateItem.origin.set(this.options.showOrigin,transition,_cb)};Lightbox.prototype.hide=function hide(transition,callback){if(!this._showing)return;this._showing=false;if(transition instanceof Function){callback=transition;transition=undefined}var node=this.nodes[this.nodes.length-1];var transform=this.transforms[this.transforms.length-1];var stateItem=this.states[this.states.length-1];var _cb=Utility.after(3,function(){this.nodes.splice(this.nodes.indexOf(node),1);this.states.splice(this.states.indexOf(stateItem),1);this.transforms.splice(this.transforms.indexOf(transform),1);if(callback)callback.call(this)}.bind(this));if(!transition)transition=this.options.outTransition;stateItem.transform.set(this.options.outTransform,transition,_cb);stateItem.opacity.set(this.options.outOpacity,transition,_cb);stateItem.origin.set(this.options.outOrigin,transition,_cb)};Lightbox.prototype.render=function render(){var result=[];for(var i=0;i<this.nodes.length;i++){result.push(this.nodes[i].render())}return result};module.exports=Lightbox});define("famous/physics/forces/Drag",["require","exports","module","./Force"],function(require,exports,module){var Force=require("./Force");function Drag(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS);if(options)this.setOptions(options);Force.call(this)}Drag.prototype=Object.create(Force.prototype);Drag.prototype.constructor=Drag;Drag.FORCE_FUNCTIONS={LINEAR:function(velocity){return velocity},QUADRATIC:function(velocity){return velocity.mult(velocity.norm())}};Drag.DEFAULT_OPTIONS={strength:.01,forceFunction:Drag.FORCE_FUNCTIONS.LINEAR};Drag.prototype.applyForce=function applyForce(targets){var strength=this.options.strength;var forceFunction=this.options.forceFunction;var force=this.force;for(var index=0;index<targets.length;index++){var particle=targets[index];forceFunction(particle.velocity).mult(-strength).put(force);particle.applyForce(force)}};Drag.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};module.exports=Drag});define("famous/views/Scroller",["require","exports","module","famous/core/Entity","famous/core/Group","famous/core/OptionsManager","famous/core/Transform","famous/utilities/Utility","famous/core/ViewSequence","famous/core/EventHandler"],function(require,exports,module){var Entity=require("famous/core/Entity");var Group=require("famous/core/Group");var OptionsManager=require("famous/core/OptionsManager");var Transform=require("famous/core/Transform");var Utility=require("famous/utilities/Utility");var ViewSequence=require("famous/core/ViewSequence");var EventHandler=require("famous/core/EventHandler");function Scroller(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this._optionsManager.setOptions(options);this._node=null;this._position=0;this._positionOffset=0;this._positionGetter=null;this._outputFunction=null;this._masterOutputFunction=null;this.outputFrom();this._onEdge=0;this.group=new Group;this.group.add({render:_innerRender.bind(this)});this._entityId=Entity.register(this);this._size=[undefined,undefined];this._contextSize=[undefined,undefined];this._eventInput=new EventHandler;this._eventOutput=new EventHandler;EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput)}Scroller.DEFAULT_OPTIONS={direction:Utility.Direction.Y,margin:0,clipSize:undefined,groupScroll:false};function _sizeForDir(size){if(!size)size=this._contextSize;var dimension=this.options.direction===Utility.Direction.X?0:1;return size[dimension]===undefined?this._contextSize[dimension]:size[dimension]}function _output(node,offset,target){var size=node.getSize?node.getSize():this._contextSize;var transform=this._outputFunction(offset);target.push({transform:transform,target:node.render()});return _sizeForDir.call(this,size)}function _getClipSize(){if(this.options.clipSize)return this.options.clipSize;else return _sizeForDir.call(this,this._contextSize)}Scroller.prototype.setOptions=function setOptions(options){this._optionsManager.setOptions(options);if(this.options.groupScroll){this.group.pipe(this._eventOutput)}else{this.group.unpipe(this._eventOutput)}};Scroller.prototype.onEdge=function onEdge(){return this._onEdge};Scroller.prototype.outputFrom=function outputFrom(fn,masterFn){if(!fn){fn=function(offset){return this.options.direction===Utility.Direction.X?Transform.translate(offset,0):Transform.translate(0,offset)}.bind(this);if(!masterFn)masterFn=fn}this._outputFunction=fn;this._masterOutputFunction=masterFn?masterFn:function(offset){return Transform.inverse(fn(-offset))}};Scroller.prototype.positionFrom=function positionFrom(position){if(position instanceof Function)this._positionGetter=position;else if(position&&position.get)this._positionGetter=position.get.bind(position);else{this._positionGetter=null;this._position=position}if(this._positionGetter)this._position=this._positionGetter.call(this)};Scroller.prototype.sequenceFrom=function sequenceFrom(node){if(node instanceof Array)node=new ViewSequence({array:node});this._node=node;this._positionOffset=0};Scroller.prototype.getSize=function getSize(actual){return actual?this._contextSize:this._size};Scroller.prototype.render=function render(){if(!this._node)return null;if(this._positionGetter)this._position=this._positionGetter.call(this);return this._entityId};Scroller.prototype.commit=function commit(context){var transform=context.transform;var opacity=context.opacity;var origin=context.origin;var size=context.size;if(!this.options.clipSize&&(size[0]!==this._contextSize[0]||size[1]!==this._contextSize[1])){this._onEdge=0;this._contextSize[0]=size[0];this._contextSize[1]=size[1];if(this.options.direction===Utility.Direction.X){this._size[0]=_getClipSize.call(this);this._size[1]=undefined}else{this._size[0]=undefined;this._size[1]=_getClipSize.call(this)}}var scrollTransform=this._masterOutputFunction(-this._position);return{transform:Transform.multiply(transform,scrollTransform),size:size,opacity:opacity,origin:origin,target:this.group.render()}};function _normalizeState(){var nodeSize=_sizeForDir.call(this,this._node.getSize());var nextNode=this._node&&this._node.getNext?this._node.getNext():null;while(nextNode&&this._position+this._positionOffset>=nodeSize){this._positionOffset-=nodeSize;this._node=nextNode;nodeSize=_sizeForDir.call(this,this._node.getSize());nextNode=this._node&&this._node.getNext?this._node.getNext():null}var prevNode=this._node&&this._node.getPrevious?this._node.getPrevious():null;while(prevNode&&this._position+this._positionOffset<0){var prevNodeSize=_sizeForDir.call(this,prevNode.getSize());this._positionOffset+=prevNodeSize;this._node=prevNode;prevNode=this._node&&this._node.getPrevious?this._node.getPrevious():null}}function _innerRender(){var size=null;var position=this._position;var result=[];this._onEdge=0;var offset=-this._positionOffset;var clipSize=_getClipSize.call(this);var currNode=this._node;while(currNode&&offset-position<clipSize+this.options.margin){offset+=_output.call(this,currNode,offset,result);currNode=currNode.getNext?currNode.getNext():null}var sizeNode=this._node;var nodesSize=_sizeForDir.call(this,sizeNode.getSize());if(offset<clipSize){while(sizeNode&&nodesSize<clipSize){sizeNode=sizeNode.getPrevious();if(sizeNode)nodesSize+=_sizeForDir.call(this,sizeNode.getSize())}sizeNode=this._node;while(sizeNode&&nodesSize<clipSize){sizeNode=sizeNode.getNext();if(sizeNode)nodesSize+=_sizeForDir.call(this,sizeNode.getSize())}}var edgeSize=nodesSize!==undefined&&nodesSize<clipSize?nodesSize:clipSize;if(!currNode&&offset-position<=edgeSize){this._onEdge=1;this._eventOutput.emit("edgeHit",{position:offset-edgeSize})}else if(!this._node.getPrevious()&&position<=0){this._onEdge=-1;this._eventOutput.emit("edgeHit",{position:0})}currNode=this._node&&this._node.getPrevious?this._node.getPrevious():null;offset=-this._positionOffset;if(currNode){size=currNode.getSize?currNode.getSize():this._contextSize;offset-=_sizeForDir.call(this,size)}while(currNode&&offset-position>-(_getClipSize.call(this)+this.options.margin)){_output.call(this,currNode,offset,result);currNode=currNode.getPrevious?currNode.getPrevious():null;if(currNode){size=currNode.getSize?currNode.getSize():this._contextSize;offset-=_sizeForDir.call(this,size)}}_normalizeState.call(this);return result}module.exports=Scroller});define("famous/views/Scrollview",["require","exports","module","famous/physics/PhysicsEngine","famous/physics/bodies/Particle","famous/physics/forces/Drag","famous/physics/forces/Spring","famous/core/EventHandler","famous/core/OptionsManager","famous/core/ViewSequence","famous/views/Scroller","famous/utilities/Utility","famous/inputs/GenericSync","famous/inputs/ScrollSync","famous/inputs/TouchSync"],function(require,exports,module){var PhysicsEngine=require("famous/physics/PhysicsEngine");var Particle=require("famous/physics/bodies/Particle");var Drag=require("famous/physics/forces/Drag");var Spring=require("famous/physics/forces/Spring");var EventHandler=require("famous/core/EventHandler");var OptionsManager=require("famous/core/OptionsManager");var ViewSequence=require("famous/core/ViewSequence");var Scroller=require("famous/views/Scroller");var Utility=require("famous/utilities/Utility");var GenericSync=require("famous/inputs/GenericSync");var ScrollSync=require("famous/inputs/ScrollSync");var TouchSync=require("famous/inputs/TouchSync");GenericSync.register({scroll:ScrollSync,touch:TouchSync});function Scrollview(options){this.options=Object.create(Scrollview.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);this._node=null;this._physicsEngine=new PhysicsEngine;this._particle=new Particle;this._physicsEngine.addBody(this._particle);this.spring=new Spring({anchor:[0,0,0]});this.drag=new Drag({forceFunction:Drag.FORCE_FUNCTIONS.QUADRATIC});this.friction=new Drag({forceFunction:Drag.FORCE_FUNCTIONS.LINEAR});this.sync=new GenericSync(["scroll","touch"],{direction:this.options.direction});this._eventInput=new EventHandler;this._eventOutput=new EventHandler;this._eventInput.pipe(this.sync);this.sync.pipe(this._eventInput);EventHandler.setInputHandler(this,this._eventInput);EventHandler.setOutputHandler(this,this._eventOutput);this._touchCount=0;this._springState=0;this._onEdge=0;this._pageSpringPosition=0;this._edgeSpringPosition=0;this._touchVelocity=undefined;this._earlyEnd=false;this._needsPaginationCheck=false;this._scroller=new Scroller;this._scroller.positionFrom(this.getPosition.bind(this)); | |
this.setOptions(options);_bindEvents.call(this)}var TOLERANCE=.5;Scrollview.DEFAULT_OPTIONS={direction:Utility.Direction.Y,rails:true,friction:.001,drag:1e-4,edgeGrip:.5,edgePeriod:300,edgeDamp:1,margin:1e3,paginated:false,pagePeriod:500,pageDamp:.8,pageStopSpeed:10,pageSwitchSpeed:.5,speedLimit:10,groupScroll:false};var SpringStates={NONE:0,EDGE:1,PAGE:2};function _handleStart(event){this._touchCount=event.count;if(event.count===undefined)this._touchCount=1;_detachAgents.call(this);this.setVelocity(0);this._touchVelocity=0;this._earlyEnd=false}function _handleMove(event){var velocity=-event.velocity;var delta=-event.delta;if(this._onEdge&&event.slip){if(velocity<0&&this._onEdge<0||velocity>0&&this._onEdge>0){if(!this._earlyEnd){_handleEnd.call(this,event);this._earlyEnd=true}}else if(this._earlyEnd&&Math.abs(velocity)>Math.abs(this.getVelocity())){_handleStart.call(this,event)}}if(this._earlyEnd)return;this._touchVelocity=velocity;if(event.slip)this.setVelocity(velocity);else this.setPosition(this.getPosition()+delta)}function _handleEnd(event){this._touchCount=event.count||0;if(!this._touchCount){_detachAgents.call(this);if(this._onEdge)_setSpring.call(this,this._edgeSpringPosition,SpringStates.EDGE);_attachAgents.call(this);var velocity=-event.velocity;var speedLimit=this.options.speedLimit;if(event.slip)speedLimit*=this.options.edgeGrip;if(velocity<-speedLimit)velocity=-speedLimit;else if(velocity>speedLimit)velocity=speedLimit;this.setVelocity(velocity);this._touchVelocity=undefined;this._needsPaginationCheck=true}}function _bindEvents(){this._eventInput.bindThis(this);this._eventInput.on("start",_handleStart);this._eventInput.on("update",_handleMove);this._eventInput.on("end",_handleEnd);this._scroller.on("edgeHit",function(data){this._edgeSpringPosition=data.position}.bind(this))}function _attachAgents(){if(this._springState)this._physicsEngine.attach([this.spring],this._particle);else this._physicsEngine.attach([this.drag,this.friction],this._particle)}function _detachAgents(){this._springState=SpringStates.NONE;this._physicsEngine.detachAll()}function _nodeSizeForDirection(node){var direction=this.options.direction;var nodeSize=(node.getSize()||this._scroller.getSize())[direction];if(!nodeSize)nodeSize=this._scroller.getSize()[direction];return nodeSize}function _handleEdge(edgeDetected){if(!this._onEdge&&edgeDetected){this.sync.setOptions({scale:this.options.edgeGrip});if(!this._touchCount&&this._springState!==SpringStates.EDGE){_setSpring.call(this,this._edgeSpringPosition,SpringStates.EDGE)}}else if(this._onEdge&&!edgeDetected){this.sync.setOptions({scale:1});if(this._springState&&Math.abs(this.getVelocity())<.001){_detachAgents.call(this);_attachAgents.call(this)}}this._onEdge=edgeDetected}function _handlePagination(){if(!this._needsPaginationCheck)return;if(this._touchCount)return;if(this._springState===SpringStates.EDGE)return;var velocity=this.getVelocity();if(Math.abs(velocity)>=this.options.pageStopSpeed)return;var position=this.getPosition();var velocitySwitch=Math.abs(velocity)>this.options.pageSwitchSpeed;var nodeSize=_nodeSizeForDirection.call(this,this._node);var positionNext=position>.5*nodeSize;var velocityNext=velocity>0;if(positionNext&&!velocitySwitch||velocitySwitch&&velocityNext)this.goToNextPage();else _setSpring.call(this,0,SpringStates.PAGE);this._needsPaginationCheck=false}function _setSpring(position,springState){var springOptions;if(springState===SpringStates.EDGE){this._edgeSpringPosition=position;springOptions={anchor:[this._edgeSpringPosition,0,0],period:this.options.edgePeriod,dampingRatio:this.options.edgeDamp}}else if(springState===SpringStates.PAGE){this._pageSpringPosition=position;springOptions={anchor:[this._pageSpringPosition,0,0],period:this.options.pagePeriod,dampingRatio:this.options.pageDamp}}this.spring.setOptions(springOptions);if(springState&&!this._springState){_detachAgents.call(this);this._springState=springState;_attachAgents.call(this)}this._springState=springState}function _normalizeState(){var position=this.getPosition();var nodeSize=_nodeSizeForDirection.call(this,this._node);var nextNode=this._node.getNext();while(position>nodeSize+TOLERANCE&&nextNode){_shiftOrigin.call(this,-nodeSize);position-=nodeSize;this._scroller.sequenceFrom(nextNode);this._node=nextNode;nextNode=this._node.getNext();nodeSize=_nodeSizeForDirection.call(this,this._node)}var previousNode=this._node.getPrevious();var previousNodeSize;while(position<-TOLERANCE&&previousNode){previousNodeSize=_nodeSizeForDirection.call(this,previousNode);this._scroller.sequenceFrom(previousNode);this._node=previousNode;_shiftOrigin.call(this,previousNodeSize);position+=previousNodeSize;previousNode=this._node.getPrevious()}}function _shiftOrigin(amount){this._edgeSpringPosition+=amount;this._pageSpringPosition+=amount;this.setPosition(this.getPosition()+amount);if(this._springState===SpringStates.EDGE){this.spring.setOptions({anchor:[this._edgeSpringPosition,0,0]})}else if(this._springState===SpringStates.PAGE){this.spring.setOptions({anchor:[this._pageSpringPosition,0,0]})}}Scrollview.prototype.outputFrom=function outputFrom(){return this._scroller.outputFrom.apply(this._scroller,arguments)};Scrollview.prototype.getPosition=function getPosition(){return this._particle.getPosition1D()};Scrollview.prototype.setPosition=function setPosition(x){this._particle.setPosition1D(x)};Scrollview.prototype.getVelocity=function getVelocity(){return this._touchCount?this._touchVelocity:this._particle.getVelocity1D()};Scrollview.prototype.setVelocity=function setVelocity(v){this._particle.setVelocity1D(v)};Scrollview.prototype.setOptions=function setOptions(options){if(options){if(options.direction!==undefined){if(options.direction==="x")options.direction=Utility.Direction.X;else if(options.direction==="y")options.direction=Utility.Direction.Y}this._scroller.setOptions(options);this._optionsManager.setOptions(options)}this._scroller.setOptions(this.options);if(this.options.groupScroll)this._scroller.pipe(this._eventInput);else this._scroller.unpipe(this._eventInput);this.drag.setOptions({strength:this.options.drag});this.friction.setOptions({strength:this.options.friction});this.spring.setOptions({period:this.options.edgePeriod,dampingRatio:this.options.edgeDamp});this.sync.setOptions({rails:this.options.rails,direction:this.options.direction===Utility.Direction.X?GenericSync.DIRECTION_X:GenericSync.DIRECTION_Y})};Scrollview.prototype.goToPreviousPage=function goToPreviousPage(){if(!this._node)return null;var previousNode=this._node.getPrevious();if(previousNode){var currentPosition=this.getPosition();var previousNodeSize=_nodeSizeForDirection.call(this,previousNode);this._scroller.sequenceFrom(previousNode);this._node=previousNode;var previousSpringPosition=currentPosition<TOLERANCE?-previousNodeSize:0;_setSpring.call(this,previousSpringPosition,SpringStates.PAGE);_shiftOrigin.call(this,previousNodeSize)}this._eventOutput.emit("pageChange",{direction:-1});return previousNode};Scrollview.prototype.goToNextPage=function goToNextPage(){if(!this._node)return null;var nextNode=this._node.getNext();if(nextNode){var currentPosition=this.getPosition();var currentNodeSize=_nodeSizeForDirection.call(this,this._node);var nextNodeSize=_nodeSizeForDirection.call(this,nextNode);this._scroller.sequenceFrom(nextNode);this._node=nextNode;var nextSpringPosition=currentPosition>currentNodeSize-TOLERANCE?currentNodeSize+nextNodeSize:currentNodeSize;_setSpring.call(this,nextSpringPosition,SpringStates.PAGE);_shiftOrigin.call(this,-currentNodeSize)}this._eventOutput.emit("pageChange",{direction:1});return nextNode};Scrollview.prototype.sequenceFrom=function sequenceFrom(node){if(node instanceof Array)node=new ViewSequence({array:node});this._node=node;return this._scroller.sequenceFrom(node)};Scrollview.prototype.getSize=function getSize(){return this._scroller.getSize.apply(this._scroller,arguments)};Scrollview.prototype.render=function render(){if(!this._node)return null;_normalizeState.call(this);_handleEdge.call(this,this._scroller.onEdge());if(this.options.paginated)_handlePagination.call(this);return this._scroller.render()};module.exports=Scrollview});define("famous/views/ScrollContainer",["require","exports","module","famous/surfaces/ContainerSurface","famous/core/EventHandler","./Scrollview","famous/utilities/Utility","famous/core/OptionsManager"],function(require,exports,module){var ContainerSurface=require("famous/surfaces/ContainerSurface");var EventHandler=require("famous/core/EventHandler");var Scrollview=require("./Scrollview");var Utility=require("famous/utilities/Utility");var OptionsManager=require("famous/core/OptionsManager");function ScrollContainer(options){this.options=Object.create(ScrollContainer.DEFAULT_OPTIONS);this._optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this.container=new ContainerSurface(this.options.container);this.scrollview=new Scrollview(this.options.scrollview);this.container.add(this.scrollview);EventHandler.setInputHandler(this,this.scrollview);EventHandler.setOutputHandler(this,this.scrollview);this.scrollview.subscribe(this.container)}ScrollContainer.DEFAULT_OPTIONS={container:{properties:{overflow:"hidden"}},scrollview:{direction:Utility.Direction.X}};ScrollContainer.prototype.setOptions=function setOptions(options){return this._optionsManager.setOptions(options)};ScrollContainer.prototype.sequenceFrom=function sequenceFrom(){return this.scrollview.sequenceFrom.apply(this.scrollview,arguments)};ScrollContainer.prototype.render=function render(){return this.container.render.apply(this.container,arguments)};module.exports=ScrollContainer});define("famous/widgets/NavigationBar",["require","exports","module","famous/core/Scene","famous/core/Surface","famous/core/Transform","famous/core/View"],function(require,exports,module){var Scene=require("famous/core/Scene");var Surface=require("famous/core/Surface");var Transform=require("famous/core/Transform");var View=require("famous/core/View");function NavigationBar(options){View.apply(this,arguments);this.title=new Surface({classes:this.options.classes,content:this.options.content});this.back=new Surface({size:[this.options.size[1],this.options.size[1]],classes:this.options.classes,content:this.options.backContent});this.back.on("click",function(){this._eventOutput.emit("back",{})}.bind(this));this.more=new Surface({size:[this.options.size[1],this.options.size[1]],classes:this.options.classes,content:this.options.moreContent});this.more.on("click",function(){this._eventOutput.emit("more",{})}.bind(this));this.layout=new Scene({id:"master",size:this.options.size,target:[{transform:Transform.inFront,origin:[0,.5],target:this.back},{origin:[.5,.5],target:this.title},{transform:Transform.inFront,origin:[1,.5],target:this.more}]});this._add(this.layout);this._optionsManager.on("change",function(event){var key=event.id;var data=event.value;if(key==="size"){this.layout.id.master.setSize(data);this.title.setSize(data);this.back.setSize([data[1],data[1]]);this.more.setSize([data[1],data[1]])}else if(key==="backClasses"){this.back.setOptions({classes:this.options.classes.concat(this.options.backClasses)})}else if(key==="backContent"){this.back.setContent(this.options.backContent)}else if(key==="classes"){this.title.setOptions({classes:this.options.classes});this.back.setOptions({classes:this.options.classes.concat(this.options.backClasses)});this.more.setOptions({classes:this.options.classes.concat(this.options.moreClasses)})}else if(key==="content"){this.setContent(this.options.content)}else if(key==="moreClasses"){this.more.setOptions({classes:this.options.classes.concat(this.options.moreClasses)})}else if(key==="moreContent"){this.more.setContent(this.options.content)}}.bind(this))}NavigationBar.prototype=Object.create(View.prototype);NavigationBar.prototype.constructor=NavigationBar;NavigationBar.DEFAULT_OPTIONS={size:[undefined,50],backClasses:["back"],backContent:"◀",classes:["navigation"],content:"",moreClasses:["more"],moreContent:"✚"};NavigationBar.prototype.setContent=function setContent(content){return this.title.setContent(content)};module.exports=NavigationBar});define("famous/widgets/Slider",["require","exports","module","famous/core/Surface","famous/surfaces/CanvasSurface","famous/core/Transform","famous/core/EventHandler","famous/math/Utilities","famous/core/OptionsManager","famous/inputs/MouseSync","famous/inputs/TouchSync","famous/inputs/GenericSync"],function(require,exports,module){var Surface=require("famous/core/Surface");var CanvasSurface=require("famous/surfaces/CanvasSurface");var Transform=require("famous/core/Transform");var EventHandler=require("famous/core/EventHandler");var Utilities=require("famous/math/Utilities");var OptionsManager=require("famous/core/OptionsManager");var MouseSync=require("famous/inputs/MouseSync");var TouchSync=require("famous/inputs/TouchSync");var GenericSync=require("famous/inputs/GenericSync");GenericSync.register({mouse:MouseSync,touch:TouchSync});function Slider(options){this.options=Object.create(Slider.DEFAULT_OPTIONS);this.optionsManager=new OptionsManager(this.options);if(options)this.setOptions(options);this.indicator=new CanvasSurface({size:this.options.indicatorSize,classes:["slider-back"]});this.label=new Surface({size:this.options.labelSize,content:this.options.label,properties:{pointerEvents:"none"},classes:["slider-label"]});this.eventOutput=new EventHandler;this.eventInput=new EventHandler;EventHandler.setInputHandler(this,this.eventInput);EventHandler.setOutputHandler(this,this.eventOutput);var scale=(this.options.range[1]-this.options.range[0])/this.options.indicatorSize[0];this.sync=new GenericSync(["mouse","touch"],{scale:scale,direction:GenericSync.DIRECTION_X});this.indicator.pipe(this.sync);this.sync.pipe(this);this.eventInput.on("update",function(data){this.set(data.position)}.bind(this));this._drawPos=0;_updateLabel.call(this)}Slider.DEFAULT_OPTIONS={size:[200,60],indicatorSize:[200,30],labelSize:[200,30],range:[0,1],precision:2,value:0,label:"",fillColor:"rgba(170, 170, 170, 1)"};function _updateLabel(){this.label.setContent(this.options.label+'<span style="float: right">'+this.get().toFixed(this.options.precision)+"</span>")}Slider.prototype.setOptions=function setOptions(options){return this.optionsManager.setOptions(options)};Slider.prototype.get=function get(){return this.options.value};Slider.prototype.set=function set(value){if(value===this.options.value)return;this.options.value=Utilities.clamp(value,this.options.range);_updateLabel.call(this);this.eventOutput.emit("change",{value:value})};Slider.prototype.getSize=function getSize(){return this.options.size};Slider.prototype.render=function render(){var range=this.options.range;var fillSize=Math.floor((this.get()-range[0])/(range[1]-range[0])*this.options.indicatorSize[0]);if(fillSize<this._drawPos){this.indicator.getContext("2d").clearRect(fillSize,0,this._drawPos-fillSize+1,this.options.indicatorSize[1])}else if(fillSize>this._drawPos){var ctx=this.indicator.getContext("2d");ctx.fillStyle=this.options.fillColor;ctx.fillRect(this._drawPos-1,0,fillSize-this._drawPos+1,this.options.indicatorSize[1])}this._drawPos=fillSize;return{size:this.options.size,target:[{origin:[0,0],target:this.indicator.render()},{transform:Transform.translate(0,0,1),origin:[0,0],target:this.label.render()}]}};module.exports=Slider});define("famous/widgets/ToggleButton",["require","exports","module","famous/core/Surface","famous/core/EventHandler","famous/views/RenderController"],function(require,exports,module){var Surface=require("famous/core/Surface");var EventHandler=require("famous/core/EventHandler");var RenderController=require("famous/views/RenderController");function ToggleButton(options){this.options={content:"",offClasses:["off"],onClasses:["on"],size:undefined,outTransition:{curve:"easeInOut",duration:300},inTransition:{curve:"easeInOut",duration:300},toggleMode:ToggleButton.TOGGLE,crossfade:true};this._eventOutput=new EventHandler;EventHandler.setOutputHandler(this,this._eventOutput);this.offSurface=new Surface;this.offSurface.on("click",function(){if(this.options.toggleMode!==ToggleButton.OFF)this.select()}.bind(this));this.offSurface.pipe(this._eventOutput);this.onSurface=new Surface;this.onSurface.on("click",function(){if(this.options.toggleMode!==ToggleButton.ON)this.deselect()}.bind(this));this.onSurface.pipe(this._eventOutput);this.arbiter=new RenderController({overlap:this.options.crossfade});this.deselect();if(options)this.setOptions(options)}ToggleButton.OFF=0;ToggleButton.ON=1;ToggleButton.TOGGLE=2;ToggleButton.prototype.select=function select(){this.selected=true;this.arbiter.show(this.onSurface,this.options.inTransition);this._eventOutput.emit("select")};ToggleButton.prototype.deselect=function deselect(){this.selected=false;this.arbiter.show(this.offSurface,this.options.outTransition);this._eventOutput.emit("deselect")};ToggleButton.prototype.isSelected=function isSelected(){return this.selected};ToggleButton.prototype.setOptions=function setOptions(options){if(options.content!==undefined){this.options.content=options.content;this.offSurface.setContent(this.options.content);this.onSurface.setContent(this.options.content)}if(options.offClasses){this.options.offClasses=options.offClasses;this.offSurface.setClasses(this.options.offClasses)}if(options.onClasses){this.options.onClasses=options.onClasses;this.onSurface.setClasses(this.options.onClasses)}if(options.size!==undefined){this.options.size=options.size;this.onSurface.setSize(this.options.size);this.offSurface.setSize(this.options.size)}if(options.toggleMode!==undefined)this.options.toggleMode=options.toggleMode;if(options.outTransition!==undefined)this.options.outTransition=options.outTransition;if(options.inTransition!==undefined)this.options.inTransition=options.inTransition;if(options.crossfade!==undefined){this.options.crossfade=options.crossfade;this.arbiter.setOptions({overlap:this.options.crossfade})}};ToggleButton.prototype.getSize=function getSize(){return this.options.size};ToggleButton.prototype.render=function render(){return this.arbiter.render()};module.exports=ToggleButton});define("famous/widgets/TabBar",["require","exports","module","famous/utilities/Utility","famous/core/View","famous/views/GridLayout","./ToggleButton"],function(require,exports,module){var Utility=require("famous/utilities/Utility");var View=require("famous/core/View");var GridLayout=require("famous/views/GridLayout");var ToggleButton=require("./ToggleButton");function TabBar(options){View.apply(this,arguments);this.layout=new GridLayout;this.buttons=[];this._buttonIds={};this._buttonCallbacks={};this.layout.sequenceFrom(this.buttons);this._add(this.layout);this._optionsManager.on("change",_updateOptions.bind(this))}TabBar.prototype=Object.create(View.prototype);TabBar.prototype.constructor=TabBar;TabBar.DEFAULT_OPTIONS={sections:[],widget:ToggleButton,size:[undefined,50],direction:Utility.Direction.X,buttons:{toggleMode:ToggleButton.ON}};function _updateOptions(data){var id=data.id;var value=data.value;if(id==="direction"){this.layout.setOptions({dimensions:_resolveGridDimensions.call(this.buttons.length,this.options.direction)})}else if(id==="buttons"){for(var i in this.buttons){this.buttons[i].setOptions(value)}}else if(id==="sections"){for(var sectionId in this.options.sections){this.defineSection(sectionId,this.options.sections[sectionId])}}}function _resolveGridDimensions(count,direction){if(direction===Utility.Direction.X)return[count,1];else return[1,count]}TabBar.prototype.defineSection=function defineSection(id,content){var button;var i=this._buttonIds[id];if(i===undefined){i=this.buttons.length;this._buttonIds[id]=i;var widget=this.options.widget;button=new widget;this.buttons[i]=button;this.layout.setOptions({dimensions:_resolveGridDimensions(this.buttons.length,this.options.direction)})}else{button=this.buttons[i];button.unbind("select",this._buttonCallbacks[id])}if(this.options.buttons)button.setOptions(this.options.buttons);button.setOptions(content);this._buttonCallbacks[id]=this.select.bind(this,id);button.on("select",this._buttonCallbacks[id])};TabBar.prototype.select=function select(id){var btn=this._buttonIds[id];if(this.buttons[btn]&&this.buttons[btn].isSelected()){this._eventOutput.emit("select",{id:id})}else if(this.buttons[btn]){this.buttons[btn].select()}for(var i=0;i<this.buttons.length;i++){if(i!==btn)this.buttons[i].deselect()}};module.exports=TabBar});define("famous/physics/bodies/Body",["require","exports","module","./Particle","famous/core/Transform","famous/math/Vector","famous/math/Quaternion","famous/math/Matrix"],function(require,exports,module){var Particle=require("./Particle");var Transform=require("famous/core/Transform");var Vector=require("famous/math/Vector");var Quaternion=require("famous/math/Quaternion");var Matrix=require("famous/math/Matrix");function Body(options){Particle.call(this,options);options=options||{};this.orientation=new Quaternion;this.angularVelocity=new Vector;this.angularMomentum=new Vector;this.torque=new Vector;if(options.orientation)this.orientation.set(options.orientation);if(options.angularVelocity)this.angularVelocity.set(options.angularVelocity);if(options.angularMomentum)this.angularMomentum.set(options.angularMomentum);if(options.torque)this.torque.set(options.torque);this.setMomentsOfInertia();this.angularVelocity.w=0;this.pWorld=new Vector}Body.DEFAULT_OPTIONS=Particle.DEFAULT_OPTIONS;Body.DEFAULT_OPTIONS.orientation=[0,0,0,1];Body.DEFAULT_OPTIONS.angularVelocity=[0,0,0];Body.AXES=Particle.AXES;Body.SLEEP_TOLERANCE=Particle.SLEEP_TOLERANCE;Body.INTEGRATOR=Particle.INTEGRATOR;Body.prototype=Object.create(Particle.prototype);Body.prototype.constructor=Body;Body.prototype.isBody=true;Body.prototype.setMass=function setMass(){Particle.prototype.setMass.apply(this,arguments);this.setMomentsOfInertia()};Body.prototype.setMomentsOfInertia=function setMomentsOfInertia(){this.inertia=new Matrix;this.inverseInertia=new Matrix};Body.prototype.updateAngularVelocity=function updateAngularVelocity(){this.angularVelocity.set(this.inverseInertia.vectorMultiply(this.angularMomentum))};Body.prototype.toWorldCoordinates=function toWorldCoordinates(localPosition){return this.pWorld.set(this.orientation.rotateVector(localPosition))};Body.prototype.getEnergy=function getEnergy(){return Particle.prototype.getEnergy.call(this)+.5*this.inertia.vectorMultiply(this.angularVelocity).dot(this.angularVelocity)};Body.prototype.reset=function reset(p,v,q,L){Particle.prototype.reset.call(this,p,v);this.angularVelocity.clear();this.setOrientation(q||[1,0,0,0]);this.setAngularMomentum(L||[0,0,0])};Body.prototype.setOrientation=function setOrientation(q){this.orientation.set(q)};Body.prototype.setAngularVelocity=function setAngularVelocity(w){this.wake();this.angularVelocity.set(w)};Body.prototype.setAngularMomentum=function setAngularMomentum(L){this.wake();this.angularMomentum.set(L)};Body.prototype.applyForce=function applyForce(force,location){Particle.prototype.applyForce.call(this,force);if(location!==undefined)this.applyTorque(location.cross(force))};Body.prototype.applyTorque=function applyTorque(torque){this.wake();this.torque.set(this.torque.add(torque))};Body.prototype.getTransform=function getTransform(){return Transform.thenMove(this.orientation.getTransform(),Transform.getTranslate(Particle.prototype.getTransform.call(this)))};Body.prototype._integrate=function _integrate(dt){Particle.prototype._integrate.call(this,dt);this.integrateAngularMomentum(dt);this.updateAngularVelocity(dt);this.integrateOrientation(dt)};Body.prototype.integrateAngularMomentum=function integrateAngularMomentum(dt){Body.INTEGRATOR.integrateAngularMomentum(this,dt)};Body.prototype.integrateOrientation=function integrateOrientation(dt){Body.INTEGRATOR.integrateOrientation(this,dt)};module.exports=Body});define("famous/physics/bodies/Circle",["require","exports","module","./Body","famous/math/Matrix"],function(require,exports,module){var Body=require("./Body");var Matrix=require("famous/math/Matrix");function Circle(options){options=options||{};this.setRadius(options.radius||0);Body.call(this,options)}Circle.prototype=Object.create(Body.prototype);Circle.prototype.constructor=Circle;Circle.prototype.setRadius=function setRadius(r){this.radius=r;this.size=[2*this.radius,2*this.radius];this.setMomentsOfInertia()};Circle.prototype.setMomentsOfInertia=function setMomentsOfInertia(){var m=this.mass;var r=this.radius;this.inertia=new Matrix([[.25*m*r*r,0,0],[0,.25*m*r*r,0],[0,0,.5*m*r*r]]);this.inverseInertia=new Matrix([[4/(m*r*r),0,0],[0,4/(m*r*r),0],[0,0,2/(m*r*r)]])};module.exports=Circle});define("famous/physics/bodies/Rectangle",["require","exports","module","./Body","famous/math/Matrix"],function(require,exports,module){var Body=require("./Body");var Matrix=require("famous/math/Matrix");function Rectangle(options){options=options||{};this.size=options.size||[0,0];Body.call(this,options)}Rectangle.prototype=Object.create(Body.prototype);Rectangle.prototype.constructor=Rectangle;Rectangle.prototype.setSize=function setSize(size){this.size=size;this.setMomentsOfInertia()};Rectangle.prototype.setMomentsOfInertia=function setMomentsOfInertia(){var m=this.mass;var w=this.size[0];var h=this.size[1];this.inertia=new Matrix([[m*h*h/12,0,0],[0,m*w*w/12,0],[0,0,m*(w*w+h*h)/12]]);this.inverseInertia=new Matrix([[12/(m*h*h),0,0],[0,12/(m*w*w),0],[0,0,12/(m*(w*w+h*h))]])};module.exports=Rectangle});define("famous/physics/constraints/Collision",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Collision(options){this.options=Object.create(Collision.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.normal=new Vector;this.pDiff=new Vector;this.vDiff=new Vector;this.impulse1=new Vector;this.impulse2=new Vector;Constraint.call(this)}Collision.prototype=Object.create(Constraint.prototype);Collision.prototype.constructor=Collision;Collision.DEFAULT_OPTIONS={restitution:.5,drift:.5,slop:0};function _normalVelocity(particle1,particle2){return particle1.velocity.dot(particle2.velocity)}Collision.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};Collision.prototype.applyConstraint=function applyConstraint(targets,source,dt){if(source===undefined)return;var v1=source.velocity;var p1=source.position;var w1=source.inverseMass;var r1=source.radius;var options=this.options;var drift=options.drift;var slop=-options.slop;var restitution=options.restitution;var n=this.normal;var pDiff=this.pDiff;var vDiff=this.vDiff;var impulse1=this.impulse1;var impulse2=this.impulse2;for(var i=0;i<targets.length;i++){var target=targets[i];if(target===source)continue;var v2=target.velocity;var p2=target.position;var w2=target.inverseMass;var r2=target.radius;pDiff.set(p2.sub(p1));vDiff.set(v2.sub(v1));var dist=pDiff.norm();var overlap=dist-(r1+r2);var effMass=1/(w1+w2);var gamma=0;if(overlap<0){n.set(pDiff.normalize());if(this._eventOutput){var collisionData={target:target,source:source,overlap:overlap,normal:n};this._eventOutput.emit("preCollision",collisionData);this._eventOutput.emit("collision",collisionData)}var lambda=overlap<=slop?((1+restitution)*n.dot(vDiff)+drift/dt*(overlap-slop))/(gamma+dt/effMass):(1+restitution)*n.dot(vDiff)/(gamma+dt/effMass);n.mult(dt*lambda).put(impulse1);impulse1.mult(-1).put(impulse2);source.applyImpulse(impulse1);target.applyImpulse(impulse2);if(this._eventOutput)this._eventOutput.emit("postCollision",collisionData)}}};module.exports=Collision});define("famous/physics/constraints/Curve",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Curve(options){this.options=Object.create(Curve.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.J=new Vector;this.impulse=new Vector;Constraint.call(this)}Curve.prototype=Object.create(Constraint.prototype);Curve.prototype.constructor=Curve;var epsilon=1e-7;var pi=Math.PI;Curve.DEFAULT_OPTIONS={equation:function(x,y,z){return 0},plane:function(x,y,z){return z},period:0,dampingRatio:0};Curve.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};Curve.prototype.applyConstraint=function applyConstraint(targets,source,dt){var options=this.options;var impulse=this.impulse;var J=this.J;var f=options.equation;var g=options.plane;var dampingRatio=options.dampingRatio;var period=options.period;for(var i=0;i<targets.length;i++){var body=targets[i];var v=body.velocity;var p=body.position;var m=body.mass;var gamma;var beta;if(period===0){gamma=0;beta=1}else{var c=4*m*pi*dampingRatio/period;var k=4*m*pi*pi/(period*period);gamma=1/(c+dt*k);beta=dt*k/(c+dt*k)}var x=p.x;var y=p.y;var z=p.z;var f0=f(x,y,z);var dfx=(f(x+epsilon,p,p)-f0)/epsilon;var dfy=(f(x,y+epsilon,p)-f0)/epsilon;var dfz=(f(x,y,p+epsilon)-f0)/epsilon;var g0=g(x,y,z);var dgx=(g(x+epsilon,y,z)-g0)/epsilon;var dgy=(g(x,y+epsilon,z)-g0)/epsilon;var dgz=(g(x,y,z+epsilon)-g0)/epsilon;J.setXYZ(dfx+dgx,dfy+dgy,dfz+dgz);var antiDrift=beta/dt*(f0+g0);var lambda=-(J.dot(v)+antiDrift)/(gamma+dt*J.normSquared()/m);impulse.set(J.mult(dt*lambda));body.applyImpulse(impulse)}};module.exports=Curve});define("famous/physics/constraints/Distance",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Distance(options){this.options=Object.create(this.constructor.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.impulse=new Vector;this.normal=new Vector;this.diffP=new Vector;this.diffV=new Vector;Constraint.call(this)}Distance.prototype=Object.create(Constraint.prototype);Distance.prototype.constructor=Distance;Distance.DEFAULT_OPTIONS={anchor:null,length:0,minLength:0,period:0,dampingRatio:0};var pi=Math.PI;Distance.prototype.setOptions=function setOptions(options){if(options.anchor){if(options.anchor.position instanceof Vector)this.options.anchor=options.anchor.position;if(options.anchor instanceof Vector)this.options.anchor=options.anchor;if(options.anchor instanceof Array)this.options.anchor=new Vector(options.anchor)}if(options.length!==undefined)this.options.length=options.length;if(options.dampingRatio!==undefined)this.options.dampingRatio=options.dampingRatio;if(options.period!==undefined)this.options.period=options.period;if(options.minLength!==undefined)this.options.minLength=options.minLength};function _calcError(impulse,body){return body.mass*impulse.norm()}Distance.prototype.setAnchor=function setAnchor(anchor){if(!this.options.anchor)this.options.anchor=new Vector;this.options.anchor.set(anchor)};Distance.prototype.applyConstraint=function applyConstraint(targets,source,dt){var n=this.normal;var diffP=this.diffP;var diffV=this.diffV;var impulse=this.impulse;var options=this.options;var dampingRatio=options.dampingRatio;var period=options.period;var minLength=options.minLength;var p2;var w2;if(source){var v2=source.velocity;p2=source.position;w2=source.inverseMass}else{p2=this.options.anchor;w2=0}var length=this.options.length;for(var i=0;i<targets.length;i++){var body=targets[i];var v1=body.velocity;var p1=body.position;var w1=body.inverseMass;diffP.set(p1.sub(p2));n.set(diffP.normalize());var dist=diffP.norm()-length;if(Math.abs(dist)<minLength)return;if(source)diffV.set(v1.sub(v2));else diffV.set(v1);var effMass=1/(w1+w2);var gamma;var beta;if(period===0){gamma=0;beta=1}else{var c=4*effMass*pi*dampingRatio/period;var k=4*effMass*pi*pi/(period*period);gamma=1/(c+dt*k);beta=dt*k/(c+dt*k)}var antiDrift=beta/dt*dist;var lambda=-(n.dot(diffV)+antiDrift)/(gamma+dt/effMass);impulse.set(n.mult(dt*lambda)); | |
body.applyImpulse(impulse);if(source)source.applyImpulse(impulse.mult(-1))}};module.exports=Distance});define("famous/physics/constraints/Surface",["require","exports","module","./Constraint","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Vector=require("famous/math/Vector");function Surface(options){this.options=Object.create(Surface.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.J=new Vector;this.impulse=new Vector;Constraint.call(this)}Surface.prototype=Object.create(Constraint.prototype);Surface.prototype.constructor=Surface;Surface.DEFAULT_OPTIONS={equation:undefined,period:0,dampingRatio:0};var epsilon=1e-7;var pi=Math.PI;Surface.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};Surface.prototype.applyConstraint=function applyConstraint(targets,source,dt){var impulse=this.impulse;var J=this.J;var options=this.options;var f=options.equation;var dampingRatio=options.dampingRatio;var period=options.period;for(var i=0;i<targets.length;i++){var particle=targets[i];var v=particle.velocity;var p=particle.position;var m=particle.mass;var gamma;var beta;if(period===0){gamma=0;beta=1}else{var c=4*m*pi*dampingRatio/period;var k=4*m*pi*pi/(period*period);gamma=1/(c+dt*k);beta=dt*k/(c+dt*k)}var x=p.x;var y=p.y;var z=p.z;var f0=f(x,y,z);var dfx=(f(x+epsilon,p,p)-f0)/epsilon;var dfy=(f(x,y+epsilon,p)-f0)/epsilon;var dfz=(f(x,y,p+epsilon)-f0)/epsilon;J.setXYZ(dfx,dfy,dfz);var antiDrift=beta/dt*f0;var lambda=-(J.dot(v)+antiDrift)/(gamma+dt*J.normSquared()/m);impulse.set(J.mult(dt*lambda));particle.applyImpulse(impulse)}};module.exports=Surface});define("famous/physics/constraints/Walls",["require","exports","module","./Constraint","./Wall","famous/math/Vector"],function(require,exports,module){var Constraint=require("./Constraint");var Wall=require("./Wall");var Vector=require("famous/math/Vector");function Walls(options){this.options=Object.create(Walls.DEFAULT_OPTIONS);if(options)this.setOptions(options);_createComponents.call(this,options.sides||this.options.sides);Constraint.call(this)}Walls.prototype=Object.create(Constraint.prototype);Walls.prototype.constructor=Walls;Walls.ON_CONTACT=Wall.ON_CONTACT;Walls.SIDES={LEFT:0,RIGHT:1,TOP:2,BOTTOM:3,FRONT:4,BACK:5,TWO_DIMENSIONAL:[0,1,2,3],THREE_DIMENSIONAL:[0,1,2,3,4,5]};Walls.DEFAULT_OPTIONS={sides:Walls.SIDES.TWO_DIMENSIONAL,size:[window.innerWidth,window.innerHeight,0],origin:[.5,.5,.5],drift:.5,slop:0,restitution:.5,onContact:Walls.ON_CONTACT.REFLECT};var _SIDE_NORMALS={0:new Vector(1,0,0),1:new Vector(-1,0,0),2:new Vector(0,1,0),3:new Vector(0,-1,0),4:new Vector(0,0,1),5:new Vector(0,0,-1)};function _getDistance(side,size,origin){var distance;var SIDES=Walls.SIDES;switch(parseInt(side)){case SIDES.LEFT:distance=size[0]*origin[0];break;case SIDES.TOP:distance=size[1]*origin[1];break;case SIDES.FRONT:distance=size[2]*origin[2];break;case SIDES.RIGHT:distance=size[0]*(1-origin[0]);break;case SIDES.BOTTOM:distance=size[1]*(1-origin[1]);break;case SIDES.BACK:distance=size[2]*(1-origin[2]);break}return distance}Walls.prototype.setOptions=function setOptions(options){var resizeFlag=false;if(options.restitution!==undefined)_setOptionsForEach.call(this,{restitution:options.restitution});if(options.drift!==undefined)_setOptionsForEach.call(this,{drift:options.drift});if(options.slop!==undefined)_setOptionsForEach.call(this,{slop:options.slop});if(options.onContact!==undefined)_setOptionsForEach.call(this,{onContact:options.onContact});if(options.size!==undefined)resizeFlag=true;if(options.sides!==undefined)this.options.sides=options.sides;if(options.origin!==undefined)resizeFlag=true;if(resizeFlag)this.setSize(options.size,options.origin)};function _createComponents(sides){this.components={};var components=this.components;for(var i=0;i<sides.length;i++){var side=sides[i];components[i]=new Wall({normal:_SIDE_NORMALS[side].clone(),distance:_getDistance(side,this.options.size,this.options.origin)})}}Walls.prototype.setSize=function setSize(size,origin){origin=origin||this.options.origin;if(origin.length<3)origin[2]=.5;this.forEach(function(wall,side){var d=_getDistance(side,size,origin);wall.setOptions({distance:d})});this.options.size=size;this.options.origin=origin};function _setOptionsForEach(options){this.forEach(function(wall){wall.setOptions(options)});for(var key in options)this.options[key]=options[key]}Walls.prototype.applyConstraint=function applyConstraint(targets,source,dt){this.forEach(function(wall){wall.applyConstraint(targets,source,dt)})};Walls.prototype.forEach=function forEach(fn){for(var key in this.sides)fn(this.sides[key],key)};Walls.prototype.rotateZ=function rotateZ(angle){this.forEach(function(wall){var n=wall.options.normal;n.rotateZ(angle).put(n)})};Walls.prototype.rotateX=function rotateX(angle){this.forEach(function(wall){var n=wall.options.normal;n.rotateX(angle).put(n)})};Walls.prototype.rotateY=function rotateY(angle){this.forEach(function(wall){var n=wall.options.normal;n.rotateY(angle).put(n)})};module.exports=Walls});define("famous/physics/forces/Repulsion",["require","exports","module","./Force","famous/math/Vector"],function(require,exports,module){var Force=require("./Force");var Vector=require("famous/math/Vector");function Repulsion(options){this.options=Object.create(Repulsion.DEFAULT_OPTIONS);if(options)this.setOptions(options);this.disp=new Vector;Force.call(this)}Repulsion.prototype=Object.create(Force.prototype);Repulsion.prototype.constructor=Repulsion;Repulsion.DECAY_FUNCTIONS={LINEAR:function(r,cutoff){return Math.max(1-1/cutoff*r,0)},MORSE:function(r,cutoff){var r0=cutoff===0?100:cutoff;var rShifted=r+r0*(1-Math.log(2));return Math.max(1-Math.pow(1-Math.exp(rShifted/r0-1),2),0)},INVERSE:function(r,cutoff){return 1/(1-cutoff+r)},GRAVITY:function(r,cutoff){return 1/(1-cutoff+r*r)}};Repulsion.DEFAULT_OPTIONS={strength:1,anchor:undefined,range:[0,Infinity],cutoff:0,cap:Infinity,decayFunction:Repulsion.DECAY_FUNCTIONS.GRAVITY};Repulsion.prototype.setOptions=function setOptions(options){if(options.anchor!==undefined){if(options.anchor.position instanceof Vector)this.options.anchor=options.anchor.position;if(options.anchor instanceof Array)this.options.anchor=new Vector(options.anchor);delete options.anchor}for(var key in options)this.options[key]=options[key]};Repulsion.prototype.applyForce=function applyForce(targets,source){var options=this.options;var force=this.force;var disp=this.disp;var strength=options.strength;var anchor=options.anchor||source.position;var cap=options.cap;var cutoff=options.cutoff;var rMin=options.range[0];var rMax=options.range[1];var decayFn=options.decayFunction;if(strength===0)return;for(var index in targets){var particle=targets[index];if(particle===source)continue;var m1=particle.mass;var p1=particle.position;disp.set(p1.sub(anchor));var r=disp.norm();if(r<rMax&&r>rMin){force.set(disp.normalize(strength*m1*decayFn(r,cutoff)).cap(cap));particle.applyForce(force)}}};module.exports=Repulsion});define("famous/physics/forces/RotationalDrag",["require","exports","module","./Drag"],function(require,exports,module){var Drag=require("./Drag");function RotationalDrag(options){Drag.call(this,options)}RotationalDrag.prototype=Object.create(Drag.prototype);RotationalDrag.prototype.constructor=RotationalDrag;RotationalDrag.DEFAULT_OPTIONS=Drag.DEFAULT_OPTIONS;RotationalDrag.FORCE_FUNCTIONS=Drag.FORCE_FUNCTIONS;RotationalDrag.FORCE_FUNCTIONS={LINEAR:function(angularVelocity){return angularVelocity},QUADRATIC:function(angularVelocity){return angularVelocity.mult(angularVelocity.norm())}};RotationalDrag.prototype.applyForce=function applyForce(targets){var strength=this.options.strength;var forceFunction=this.options.forceFunction;var force=this.force;for(var index=0;index<targets.length;index++){var particle=targets[index];forceFunction(particle.angularVelocity).mult(-100*strength).put(force);particle.applyTorque(force)}};RotationalDrag.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};module.exports=RotationalDrag});define("famous/physics/forces/RotationalSpring",["require","exports","module","./Spring"],function(require,exports,module){var Spring=require("./Spring");function RotationalSpring(options){Spring.call(this,options)}RotationalSpring.prototype=Object.create(Spring.prototype);RotationalSpring.prototype.constructor=RotationalSpring;RotationalSpring.DEFAULT_OPTIONS=Spring.DEFAULT_OPTIONS;RotationalSpring.FORCE_FUNCTIONS=Spring.FORCE_FUNCTIONS;RotationalSpring.prototype.applyForce=function applyForce(targets){var force=this.force;var options=this.options;var disp=this.disp;var stiffness=options.stiffness;var damping=options.damping;var restLength=options.length;var anchor=options.anchor;for(var i=0;i<targets.length;i++){var target=targets[i];disp.set(anchor.sub(target.orientation));var dist=disp.norm()-restLength;if(dist===0)return;var m=target.mass;stiffness*=m;damping*=m;force.set(disp.normalize(stiffness*this.forceFunction(dist,this.options.lMax)));if(damping)force.set(force.add(target.angularVelocity.mult(-damping)));target.applyTorque(force)}};RotationalSpring.prototype.getEnergy=function getEnergy(target){var options=this.options;var restLength=options.length;var anchor=options.anchor;var strength=options.stiffness;var dist=anchor.sub(target.orientation).norm()-restLength;return.5*strength*dist*dist};module.exports=RotationalSpring});define("famous/physics/forces/VectorField",["require","exports","module","./Force","famous/math/Vector"],function(require,exports,module){var Force=require("./Force");var Vector=require("famous/math/Vector");function VectorField(options){this.options=Object.create(VectorField.DEFAULT_OPTIONS);if(options)this.setOptions(options);_setFieldOptions.call(this,this.options.field);Force.call(this);this.evaluation=new Vector(0,0,0)}VectorField.prototype=Object.create(Force.prototype);VectorField.prototype.constructor=VectorField;VectorField.FIELDS={CONSTANT:function(v,options){return v.set(options.direction)},LINEAR:function(v){return v},RADIAL:function(v){return v.set(v.mult(-1,v))},SPHERE_ATTRACTOR:function(v,options){return v.set(v.mult((options.radius-v.norm())/v.norm()))},POINT_ATTRACTOR:function(v,options){return v.set(options.position.sub(v))}};VectorField.DEFAULT_OPTIONS={strength:1,field:VectorField.FIELDS.CONSTANT};VectorField.prototype.setOptions=function setOptions(options){for(var key in options)this.options[key]=options[key]};function _setFieldOptions(field){var FIELDS=VectorField.FIELDS;switch(field){case FIELDS.CONSTANT:if(!this.options.direction)this.options.direction=new Vector(0,1,0);break;case FIELDS.POINT_ATTRACTOR:if(!this.options.position)this.options.position=new Vector(0,0,0);break;case FIELDS.SPHERE_ATTRACTOR:if(!this.options.radius)this.options.radius=1;break}}function _evaluate(v){var evaluation=this.evaluation;var field=this.options.field;evaluation.set(v);return field(evaluation,this.options)}VectorField.prototype.applyForce=function applyForce(targets){var force=this.force;for(var i=0;i<targets.length;i++){var particle=targets[i];force.set(_evaluate.call(this,particle.position).mult(particle.mass*this.options.strength));particle.applyForce(force)}};module.exports=VectorField});(function(){function loadcss(filename){var fileref=document.createElement("link");fileref.setAttribute("rel","stylesheet");fileref.setAttribute("type","text/css");fileref.setAttribute("href",filename);document.getElementsByTagName("head")[0].appendChild(fileref)}loadcss("http://code.famo.us/famous/0.2/famous.css");window.addEventListener("load",function(){console.log("----------- start -----------");require(["main"])})})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment