From 3ac71b1195a2d8c9295a2407b7476aa9d50cb8cd Mon Sep 17 00:00:00 2001 From: David Perit Date: Fri, 24 Aug 2012 10:59:30 -0400 Subject: [PATCH 1/4] Added newer version of gladius Updated screenshot --- examples/tank.png | Bin 23143 -> 20580 bytes gladius/README.md | 4 +- .../examples/camera-rotate/camera-rotate.js | 8 +- .../examples/cube-collision/cube-collision.js | 17 +- gladius/examples/cube-impulse/cube-impulse.js | 11 +- gladius/examples/cube/cube.js | 8 +- gladius/examples/tank/index.html | 4 +- gladius/examples/tank/tank-controls.json | 4 +- gladius/examples/tank/tank.js | 531 +- gladius/gladius-box2d.js | 12167 +++++++++++++-- gladius/gladius-core.js | 12443 +++++++++++++-- gladius/gladius-cubicvr.js | 12523 ++++++++++++++-- gladius/package.json | 3 +- 13 files changed, 33023 insertions(+), 4700 deletions(-) diff --git a/examples/tank.png b/examples/tank.png index 48eec1cc2197e4fd6e18fb7b3d3dac4029cd8dc7..bde48660aa089464d7adcc24151a27ce3e060513 100644 GIT binary patch literal 20580 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 Dia)GA literal 23143 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 1.5){ + if (cubeY > 1.5){ var impEvent = new engine.Event('LinearImpulse',{impulse: [getRandom(0,0.1) * -1, getRandom(0, 1) * -1]}); impEvent.dispatch(parentCube); } - if (cubePosition[0] < -1.5){ + if (cubeX < -1.5){ var impEvent = new engine.Event('LinearImpulse',{impulse: [getRandom(0,1), 0]}); impEvent.dispatch(parentCube); var angEvent = new engine.Event('AngularImpulse',{impulse: getRandom(0, 0.1)}); angEvent.dispatch(parentCube); } - if (cubePosition[0] > 1.5){ + if (cubeX > 1.5){ var impEvent = new engine.Event('LinearImpulse',{impulse: [getRandom(0, 1) * -1, 0]}); impEvent.dispatch(parentCube); diff --git a/gladius/examples/cube/cube.js b/gladius/examples/cube/cube.js index 3cb7aa3..b9fd6b2 100644 --- a/gladius/examples/cube/cube.js +++ b/gladius/examples/cube/cube.js @@ -99,13 +99,9 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.findNamed( "light-marker" ).setParent( space.findNamed( "light-center" ) ); var task = new engine.FunctionTask( function() { - var cubeRotation = new engine.math.Vector3( space.findNamed( "cube" ).findComponent( "Transform" ).rotation ); - cubeRotation = engine.math.vector3.add( cubeRotation, [space.clock.delta * 0.003, space.clock.delta * 0.001, space.clock.delta * 0.0007] ); - space.findNamed( "cube" ).findComponent( "Transform" ).setRotation( cubeRotation ); + space.findNamed( "cube" ).findComponent( "Transform" ).rotation.add([space.clock.delta * 0.003, space.clock.delta * 0.001, space.clock.delta * 0.0007]); - var lightRotation = new engine.math.Vector3( space.findNamed( "light-center" ).findComponent( "Transform" ).rotation ); - lightRotation = engine.math.vector3.add( lightRotation, [0, space.clock.delta * 0.001, 0] ); - space.findNamed( "light-center" ).findComponent( "Transform" ).setRotation( lightRotation ); + space.findNamed( "light-center" ).findComponent( "Transform" ).rotation.y += space.clock.delta * 0.001; }, { tags: ["@update"] }); diff --git a/gladius/examples/tank/index.html b/gladius/examples/tank/index.html index 81f2028..5193d4d 100644 --- a/gladius/examples/tank/index.html +++ b/gladius/examples/tank/index.html @@ -15,7 +15,9 @@

Tank controls

Press S to move the tank backwards
Press A to turn the tank left
Press D to turn the tank right
- Press Space to trigger a fire event (viewable in the console) + Press the left arrow key to turn the turret to the left
+ Press the right arrow key to turn the turret to the right
+ Press Space to fire

diff --git a/gladius/examples/tank/tank-controls.json b/gladius/examples/tank/tank-controls.json index 21cf1e1..7d948ed 100644 --- a/gladius/examples/tank/tank-controls.json +++ b/gladius/examples/tank/tank-controls.json @@ -4,7 +4,9 @@ "MoveBackward": [ "S" ], "TurnLeft": [ "A" ], "TurnRight": [ "D" ], - "StrafeModifier": [ "SHIFT" ] + "StrafeModifier": [ "SHIFT" ], + "TurnTurretLeft": [ "LEFT" ], + "TurnTurretRight": [ "RIGHT" ] }, "Actions": { "Fire": [ "SPACE" ] diff --git a/gladius/examples/tank/tank.js b/gladius/examples/tank/tank.js index 2089095..fda977c 100644 --- a/gladius/examples/tank/tank.js +++ b/gladius/examples/tank/tank.js @@ -3,12 +3,13 @@ document.addEventListener( "DOMContentLoaded", function( e ) { require.config({ baseUrl: "../.." }); - - require( - [ "gladius-core", + + require( + [ "gladius-core", "gladius-cubicvr", - "gladius-input" ], - function( Gladius, cubicvrExtension, inputExtension ) { + "gladius-input", + "gladius-box2d" ], + function( Gladius, cubicvrExtension, inputExtension, box2dExtension ) { var engine = new Gladius(); @@ -37,19 +38,36 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } engine.registerExtension( inputExtension, inputOptions ); + //Need to find a way to make this property access longer :) + engine.registerExtension( box2dExtension, + {resolver: {dimensionMap: box2dExtension.services.resolver.service.prototype.DimensionMaps.XZ}}); + var cubicvr = engine.findExtension( "gladius-cubicvr" ); var input = engine.findExtension( "gladius-input" ); + var box2d = engine.findExtension( "gladius-box2d" ); var resources = {}; + var bulletMaterialArgs = '?colorTexture=../assets/images/cube-diffuse.jpg' + + '&bumpTexture=../assets/images/cube-bump.jpg' + + '&normalTexture=../assets/images/cube-normal.jpg'; + var materialArgs = '?colorTexture=../assets/images/tank-diffuse.jpg' + '&bumpTexture=../assets/images/tank-bump.jpg' + '&normalTexture=../assets/images/tank-normal.jpg'; + var redMaterialArgs = '?colorTexture=../assets/images/red-tank-diffuse.jpg' + + '&bumpTexture=../assets/images/tank-bump.jpg' + + '&normalTexture=../assets/images/tank-normal.jpg'; + + var wallMaterialArgs = '?colorTexture=../assets/images/cube-impulse-diffuse.jpg' + + '&bumpTexture=../assets/images/cube-impulse-bump.jpg' + + '&normalTexture=../assets/images/cube-impulse-normal.jpg'; + engine.get( [ { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=2.0&width=1.0&depth=0.5", + url: "../assets/procedural-prism.js?length=1.0&width=0.25&depth=0.5", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankBody = mesh; @@ -59,7 +77,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=1.7&width=0.4&depth=0.7", + url: "../assets/procedural-prism.js?length=0.85&width=0.35&depth=0.2", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTread = mesh; @@ -69,7 +87,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=1.0&width=0.7&depth=0.3", + url: "../assets/procedural-prism.js?length=0.5&width=0.15&depth=0.35", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTurret = mesh; @@ -79,14 +97,34 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=0.8&width=0.2&depth=0.1", + url: "../assets/procedural-prism.js?length=0.4&width=0.05&depth=0.1", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankBarrel = mesh; }, onfailure: function( error ) { } - }, + }, + { + type: cubicvr.Mesh, + url: "../assets/procedural-prism.js?length=10&width=1&depth=1", + load: engine.loaders.procedural, + onsuccess: function( mesh ) { + resources.wall = mesh; + }, + onfailure: function( error ) { + } + }, + { + type: cubicvr.Mesh, + url: "../assets/procedural-sphere.js?type=sphere&radius=0.25", + load: engine.loaders.procedural, + onsuccess: function( mesh ) { + resources.bullet = mesh; + }, + onfailure: function( error ) { + } + }, { type: cubicvr.MaterialDefinition, url: "../assets/procedural-material.js" + materialArgs, @@ -97,6 +135,36 @@ document.addEventListener( "DOMContentLoaded", function( e ) { onfailure: function( error ) { } }, + { + type: cubicvr.MaterialDefinition, + url: "../assets/procedural-material.js" + redMaterialArgs, + load: engine.loaders.procedural, + onsuccess: function( material ) { + resources.redMaterial = material; + }, + onfailure: function( error ) { + } + }, + { + type: cubicvr.MaterialDefinition, + url: "../assets/procedural-material.js" + wallMaterialArgs, + load: engine.loaders.procedural, + onsuccess: function( material ) { + resources.wallMaterial = material; + }, + onfailure: function( error ) { + } + }, + { + type: cubicvr.MaterialDefinition, + url: "../assets/procedural-material.js" + bulletMaterialArgs, + load: engine.loaders.procedural, + onsuccess: function( material ) { + resources.bulletMaterial = material; + }, + onfailure: function( error ) { + } + }, { type: input.Map, url: "tank-controls.json", @@ -112,143 +180,418 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } ); - }); + }); function game( engine, resources ) { + function getRandom(min, max) + { + return Math.random() * (max - min) + min; + } + var math = engine.math; var space = new engine.SimulationSpace(); var cubicvr = engine.findExtension( "gladius-cubicvr" ); var input = engine.findExtension( "gladius-input" ); + var box2d = engine.findExtension( "gladius-box2d" ); var Entity = engine.Entity; + var lastBulletTime = 0; + var lastRedBulletTime = 0; + var greenTankFiringInterval = 500; + var redTankFiringInterval = 1000; + var tankMovementSpeed = 3; + var tankRotationSpeed = 2; + var greenTankTurretRotationSpeed = 2; + var redTankTurretRotationSpeed = 1.1; + var minRedTankStunTime = 1; + var maxRedTankStunTime = 2.25; + var minGreenTankStunTime = 0.5; + var maxGreenTankStunTime = 1; + var bulletVelocity = [3,0,0]; + + var tankVelocity = [0,0,0]; + var rotation = 0; + var lightDefinition = new cubicvr.LightDefinition({ - intensity: 2, + intensity: 1.5, + distance: 30, light_type: cubicvr.LightDefinition.LightTypes.POINT, method: cubicvr.LightDefinition.LightingMethods.DYNAMIC }); var tankLogic = { "Update": function( event ) { - if( this.owner.hasComponent( "Controller" ) ) { + var elapsedTime = space.clock.delta/1000; + var greenTank = space.findNamed( "tank" ); + var turretTransform = space.findNamed ("tank-turret").findComponent( "Transform" ); + if (greenTank.stunned){ + greenTank.stunnedTime -= elapsedTime; + if (greenTank.stunnedTime < 0){ + greenTank.stunned = false; + }else{ + turretTransform.rotation.y += greenTank.stunnedTurretRotationSpeed * elapsedTime * greenTank.stunnedTurretRotationDirection; + } + }else if( this.owner.hasComponent( "Controller" ) ) { var controller = this.owner.findComponent( "Controller" ); - var transform = space.findNamed( "tank-body" ).findComponent( "Transform" ); + var physBody = this.owner.findComponent( "Body" ); + var transform = greenTank.findComponent( "Transform" ); + tankVelocity[0] = 0; + tankVelocity[1] = 0; + tankVelocity[2] = 0; + rotation = 0; + if( controller.states["MoveForward"] ) { - console.log( this.owner.id, "Move forward!" ); - var direction = math.transform.translate( [space.clock.delta * 0.001, 0, 0] ); - var rotation = math.transform.rotate( transform.rotation ); - direction = math.matrix4.multiply( [direction, rotation] ); - direction = [direction[12], direction[13], direction[14]]; - transform.setPosition( math.vector3.add( direction, transform.position ) ); + tankVelocity[0]+=tankMovementSpeed; } if( controller.states["MoveBackward"] ) { - console.log( this.owner.id, "Move backward!" ); - var direction = math.transform.translate( [space.clock.delta * -0.001, 0, 0] ); - var rotation = math.transform.rotate( transform.rotation ); - direction = math.matrix4.multiply( [direction, rotation] ); - direction = [direction[12], direction[13], direction[14]]; - transform.setPosition( math.vector3.add( direction, transform.position ) ); + tankVelocity[0]-=tankMovementSpeed; } if( controller.states["TurnLeft"] ) { if( controller.states["StrafeModifier"] ) { - console.log( this.owner.id, "Strafe left!" ); - var direction = math.transform.translate( [0, space.clock.delta * -0.001, 0] ); - var rotation = math.transform.rotate( transform.rotation ); - direction = math.matrix4.multiply( [direction, rotation] ); - direction = [direction[12], direction[13], direction[14]]; - transform.setPosition( math.vector3.add( direction, transform.position ) ); + tankVelocity[2]-=tankMovementSpeed; } else { - console.log( this.owner.id, "Turn left!" ); - var rotation = transform.rotation; - transform.setRotation( math.vector3.add( rotation, [0, 0, space.clock.delta * -0.001] ) ); + rotation+=tankRotationSpeed; } } if( controller.states["TurnRight"] ) { if( controller.states["StrafeModifier"] ) { - console.log( this.owner.id, "Strafe right!" ); - var direction = math.transform.translate( [0, space.clock.delta * 0.001, 0] ); - var rotation = math.transform.rotate( transform.rotation ); - direction = math.matrix4.multiply( [direction, rotation] ); - direction = [direction[12], direction[13], direction[14]]; - transform.setPosition( math.vector3.add( direction, transform.position ) ); + tankVelocity[2]+=tankMovementSpeed; } else { - console.log( this.owner.id, "Turn right!" ); - var rotation = transform.rotation; - transform.setRotation( math.vector3.add( rotation, [0, 0, space.clock.delta * 0.001] ) ); + rotation-=tankRotationSpeed; } } + transform.directionToWorld(tankVelocity, tankVelocity); + physBody.setLinearVelocity(tankVelocity[0],tankVelocity[2]); + physBody.setAngularVelocity(rotation); + if (controller.states["TurnTurretLeft"] ) { + turretTransform.rotation.add([0, (space.clock.delta/1000) * greenTankTurretRotationSpeed, 0]); + } + if (controller.states["TurnTurretRight"] ) { + turretTransform.rotation.add([0, (space.clock.delta/1000) * -greenTankTurretRotationSpeed, 0]); + } + } }, "Fire": function( event ) { - console.log( this.owner.id, "Fire!" ); + if (space.clock.time > lastBulletTime + greenTankFiringInterval){ + lastBulletTime = space.clock.time; + var physicsBody = new box2d.Body({bodyDefinition: new box2d.BodyDefinition({bullet:true}), + fixtureDefinition: new box2d.FixtureDefinition( + { + shape:new box2d.CircleShape(0.25), + friction:0.1, + //restitution determines the elasticity of collisions + //0 = inelastic, 1 = completely elastic, >1 = very high velocities + restitution:0.7, + //The collision filters here are defined using bitwise masking. + //Please see http://www.box2d.org/manual.html#_Toc258082972 for more details + //In this case the mask of 23 means it'll collide with + //tank b, walls, and with bullets from the green tank and the red tank + //which are categories 16, 4, 2, and 1 + //16+4+2+1 = 23 + //Note that all the component numbers here are and must be powers of 2 + filter:{categoryBits:2, maskBits:23} + })}); + physicsBody.tankBulletCollisions = 0; + var barrelTransform = space.findNamed("tank-barrel").findComponent( "Transform"); + var bulletFiringPoint = math.vector3.add(barrelTransform.toWorldPoint(), barrelTransform.directionToWorld([0.3,0,0])); + var newBullet = new Entity("bullet", + [ + new engine.core.Transform(bulletFiringPoint), + new cubicvr.Model(resources.bullet, resources.bulletMaterial), + physicsBody + ] + ); + physicsBody.onContactBegin = function(event){ + this.tankBulletCollisions++; + if (this.tankBulletCollisions === 3){ + //This is how you remove something from the space properly + this.owner.setActive(false); + space.remove(this.owner); + } + }; + space.add(newBullet); + bulletVelocity = [3,0,0]; + space.findNamed("tank-barrel").findComponent( "Transform").directionToWorld(bulletVelocity, bulletVelocity); + var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); + impEvent.dispatch(newBullet); + } } }; - // This parent entity will let us adjust the position and orientation of the - // tank, and handle game logic events - space.add( new Entity( "tank", - [ - new engine.core.Transform( [0, 0, 5], [math.TAU/6, 0, 0], [0.5, 0.5, 0.5] ), - new input.Controller( resources.tankControls ), - new engine.logic.Actor( tankLogic ) - ], - ["tank"] - )); - space.add( new Entity( "tank-body", - [ - new engine.core.Transform(), - new cubicvr.Model( resources.tankBody, resources.material ) - ], - ["tank"], - space.findNamed( "tank" ) - )); - space.add( new Entity( "tank-tread", + function createTank(name, position, material, hasControls, collisionCategory) { + // This parent entity will let us adjust the position and orientation of the + // tank, and handle game logic events + space.add(new Entity(name, + [ + new engine.core.Transform(position), + new engine.logic.Actor(tankLogic), + new box2d.Body({bodyDefinition: new box2d.BodyDefinition(), + fixtureDefinition: new box2d.FixtureDefinition( + {shape:new box2d.BoxShape(1,1), + filter:{categoryBits:collisionCategory} + })}) + ], + [name] + )); + if (hasControls){ + space.findNamed(name).addComponent(new input.Controller(resources.tankControls)); + } + space.add(new Entity(name + "-body", + [ + new engine.core.Transform(), + new cubicvr.Model(resources.tankBody, material) + ], + [name], + space.findNamed(name) + )); + space.add(new Entity(name + "-tread", + [ + new engine.core.Transform([0, 0, 0.4]), + new cubicvr.Model(resources.tankTread, material) + ], + [name], + space.findNamed(name + "-body") + )); + space.add(new Entity(name + "-tread", + [ + new engine.core.Transform([0, 0, -0.4]), + new cubicvr.Model(resources.tankTread, material) + ], + [name], + space.findNamed(name + "-body") + )); + space.add(new Entity(name+"-turret", + [ + new engine.core.Transform([-0.1, 0.3, 0]), + new cubicvr.Model(resources.tankTurret, material) + ], + [name], + space.findNamed(name + "-body") + )); + space.add(new Entity(name + "-barrel", + [ + new engine.core.Transform([0.4, 0, 0]), + new cubicvr.Model(resources.tankBarrel, material) + ], + [name], + space.findNamed(name + "-turret") + )); + } + createTank("tank", [-3,0,-3], resources.material, true, 8); + createTank("red-tank", [1,0,0], resources.redMaterial, false, 16); + var redTank = space.findNamed( "red-tank" ); + redTank.doneRotation = true; + redTank.doneMovement = true; + redTank.stunned = false; + redTank.stunnedTurretRotationDirection = -1; + redTank.findComponent("Body").onContactBegin = function(event){ + if (!this.owner.stunned){ + this.owner.stunnedTime = getRandom(minRedTankStunTime, maxRedTankStunTime); + this.owner.stunnedTurretRotationSpeed = math.TAU / this.owner.stunnedTime; + this.owner.stunnedTurretRotationDirection = this.owner.stunnedTurretRotationDirection * -1; + this.owner.stunned = true; + } + }; + + var greenTank = space.findNamed( "tank" ); + greenTank.stunned = false; + greenTank.stunnedTurretRotationDirection = -1; + greenTank.findComponent("Body").onContactBegin = function(event){ + if (!this.owner.stunned){ + this.owner.stunnedTime = getRandom(minGreenTankStunTime, maxGreenTankStunTime); + this.owner.stunnedTurretRotationSpeed = math.TAU / this.owner.stunnedTime; + this.owner.stunnedTurretRotationDirection = this.owner.stunnedTurretRotationDirection * -1; + this.owner.stunned = true; + } + }; + + //TODO: Make these walls have tiling textures + var bodyDefinition = new box2d.BodyDefinition({type:box2d.BodyDefinition.BodyTypes.STATIC}); + var fixtureDefinition = new box2d.FixtureDefinition({ + shape:new box2d.BoxShape(10,1), + filter:{categoryBits:1, maskBits:30}, + restitution:0.7 + }); + + var body = new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}); + + space.add( new Entity( "wallLeft", [ - new engine.core.Transform( [0, 0.8, 0] ), - new cubicvr.Model( resources.tankTread, resources.material ) - ], - ["tank"], - space.findNamed( "tank-body" ) + new engine.core.Transform([-5,0,0], [0,math.TAU/4,0]), + new cubicvr.Model(resources.wall, resources.wallMaterial), + new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) + ] )); - space.add( new Entity( "tank-tread", + space.add( new Entity( "wallRight", [ - new engine.core.Transform( [0, -0.8, 0] ), - new cubicvr.Model( resources.tankTread, resources.material ) - ], - ["tank"], - space.findNamed( "tank-body" ) + new engine.core.Transform([5,0,0], [0,math.TAU/4,0]), + new cubicvr.Model(resources.wall, resources.wallMaterial), + new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) + ] )); - space.add( new Entity( "tank-turret", + space.add( new Entity( "wallTop", [ - new engine.core.Transform( [-0.2, 0, -0.6] ), - new cubicvr.Model( resources.tankTurret, resources.material ) - ], - ["tank"], - space.findNamed( "tank-body" ) + new engine.core.Transform([0,0,-5], [0,0,0]), + new cubicvr.Model(resources.wall, resources.wallMaterial), + new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) + ] )); - space.add( new Entity( "tank-barrel", + space.add( new Entity( "wallBottom", [ - new engine.core.Transform( [0.8, 0, 0] ), - new cubicvr.Model( resources.tankBarrel, resources.material ) - ], - ["tank"], - space.findNamed( "tank-turret" ) + new engine.core.Transform([0,0,5], [0,0,0]), + new cubicvr.Model(resources.wall, resources.wallMaterial), + new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) + ] )); space.add( new Entity( "camera", [ - new engine.core.Transform( [0, 0, 0] ), - new cubicvr.Camera({ - targeted: true - }), - new cubicvr.Light() + new engine.core.Transform( [0, 10, 0], [-math.TAU/4, 0, 0] ), + new cubicvr.Camera(), + new cubicvr.Light(lightDefinition) ] )); - // space.findNamed( "camera" ).findComponent( "Camera" ).setTarget( 0, 0, 0 ); + + var elapsedTime; + var redTank = space.findNamed( "red-tank" ); + var redTankTransform = redTank.findComponent("Transform"); + var redTankTurret = space.findNamed( "red-tank-turret" ); + var redTankTurretTransform = redTankTurret.findComponent("Transform"); + var redTankBarrelTransform = space.findNamed("red-tank-barrel").findComponent("Transform"); + var position = redTankTransform.position; + var physicsBody = redTank.findComponent("Body"); + var greenTank = space.findNamed( "tank" ); + var greenTankTransform = greenTank.findComponent("Transform"); + var newPosition = []; + var changeInDirection; + + function removeExcessRotation(rotation){ + //If the change in direction is greater than 180 degrees then just rotate the other way instead + var reducedRotation = (rotation - (Math.floor(rotation / math.TAU) * math.TAU)); + if (reducedRotation > math.PI){ + reducedRotation = (math.TAU - reducedRotation) * -1; + }else if (reducedRotation < -math.PI){ + reducedRotation = (-math.TAU - reducedRotation) * -1; + } + return reducedRotation; + } var task = new engine.FunctionTask( function() { + elapsedTime = space.clock.delta/1000; + + if (redTank.stunned){ + redTank.stunnedTime -= elapsedTime; + if (redTank.stunnedTime < 0){ + redTank.stunned = false; + redTank.doneMovement = true; + }else{ + redTankTurretTransform.rotation.y += redTank.stunnedTurretRotationSpeed * elapsedTime * redTank.stunnedTurretRotationDirection; + } + } + if (!redTank.stunned){ + //Rotate the red turret to point at the green tank + var greenTankInRedTankTurretLocalSpace = redTankTurretTransform.transformToLocal(greenTankTransform); + //We are multiplying the z coordinate by -1 because Math.atan2 expects (y,x), and up on the screen is negative for z but positive for y + var directionOfGreenTank = Math.atan2(greenTankInRedTankTurretLocalSpace[2] * -1, greenTankInRedTankTurretLocalSpace[0]); + var currentDirection = redTankTurretTransform.rotation.y; + var differenceBetweenDirections = removeExcessRotation(currentDirection - directionOfGreenTank); + var newRotation; + if (differenceBetweenDirections > 0 && differenceBetweenDirections > (redTankTurretRotationSpeed * elapsedTime)){ + newRotation = currentDirection - (redTankTurretRotationSpeed * elapsedTime); + }else if (differenceBetweenDirections < 0 && (-differenceBetweenDirections) > (redTankTurretRotationSpeed * elapsedTime)){ + newRotation = currentDirection + (redTankTurretRotationSpeed * elapsedTime); + }else{ + newRotation = directionOfGreenTank; + } + redTankTurretTransform.rotation.y = newRotation; + + if (space.clock.time > lastRedBulletTime + redTankFiringInterval){ + lastRedBulletTime = space.clock.time; + var bulletBody = new box2d.Body({bodyDefinition: new box2d.BodyDefinition({bullet:true}), + fixtureDefinition: new box2d.FixtureDefinition( + { + shape:new box2d.CircleShape(0.25), + friction:0.1, + restitution:0.7, + filter:{categoryBits:2, maskBits:15} + })}); + bulletBody.tankBulletCollisions = 0; + var bulletFiringPoint = math.vector3.add(redTankBarrelTransform.toWorldPoint(), redTankBarrelTransform.directionToWorld([0.3,0,0])); + var newBullet = new Entity("bullet", + [ + new engine.core.Transform(bulletFiringPoint), + new cubicvr.Model(resources.bullet, resources.bulletMaterial), + bulletBody + ] + ); + bulletBody.onContactBegin = function(event){ + this.tankBulletCollisions++; + if (this.tankBulletCollisions === 2){ + //This is how you remove something from the space properly + this.owner.setActive(false); + space.remove(this.owner); + } + }; + space.add(newBullet); + bulletVelocity = [2,0,0]; + redTankBarrelTransform.directionToWorld(bulletVelocity, bulletVelocity); + var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); + impEvent.dispatch(newBullet); + } + + if (redTank.doneMovement){ + //Done moving, make up a new destination + newPosition[0] = getRandom(-3, 3); + newPosition[1] = position.y; + newPosition[2] = getRandom(-3, 3); + //Uncomment this to make bullets appear at the tank's new destination. Useful for debugging +// space.add(new Entity("bullet", +// [ +// new engine.core.Transform(newPosition), +// new cubicvr.Model(resources.bullet, resources.bulletMaterial) +// ] +// )); + + var currentRotation = redTankTransform.rotation.y; + var directionToNewPosition = Math.atan2(newPosition[2] - position.z, newPosition[0] - position.x); + + changeInDirection = removeExcessRotation(directionToNewPosition + currentRotation); + //Because we are only telling the tank to stop rotating/moving at the end of a frame, this does NOT result in deterministic movement + //The tank will turn/move more or less depending on how high the frame rate is + redTank.timeToRotate = Math.abs(changeInDirection)/tankRotationSpeed; + redTank.timeToMove = position.distance(newPosition) / tankMovementSpeed; + redTank.rotationDirection = changeInDirection > 0 ? -1 : 1; + redTank.doneRotation = false; + redTank.doneMovement = false; + physicsBody.setAngularVelocity(tankRotationSpeed * redTank.rotationDirection); + physicsBody.setLinearVelocity(0,0); + } + //Rotate until we reach the desired direction + if (!redTank.doneRotation){ + redTank.timeToRotate -= elapsedTime; + if (redTank.timeToRotate < 0){ + redTank.doneRotation = true; + redTank.tankVelocity = [tankMovementSpeed, 0, 0]; + redTankTransform.directionToWorld(redTank.tankVelocity, redTank.tankVelocity); + physicsBody.setAngularVelocity(0); + physicsBody.setLinearVelocity(redTank.tankVelocity[0], redTank.tankVelocity[2]); + } + } + //Move until we reach the desired destination + if (redTank.doneRotation){ + redTank.timeToMove -= elapsedTime; + if (redTank.timeToMove < 0){ + redTank.doneMovement = true; + physicsBody.setLinearVelocity(0,0); + } + } + } + }, { tags: ["@update"] - }).start(); + }); + task.start(); engine.resume(); } diff --git a/gladius/gladius-box2d.js b/gladius/gladius-box2d.js index 03bbbfc..084ceb5 100644 --- a/gladius/gladius-box2d.js +++ b/gladius/gladius-box2d.js @@ -1750,1683 +1750,10891 @@ var requirejs, require, define; define("../tools/almond", function(){}); -/*jshint white: false, strict: false, plusplus: false */ -/*global define: false */ - -//JS language helpers. - -//Array Remove - By John Resig (MIT Licensed) -//Done outside the define call since it should be immediately -//before dependency tracing is done for any module. -if ( !Array.prototype.remove ) { - Array.prototype.remove = function(from, to) { - var rest = this.slice( (to || from) + 1 || this.length ); - this.length = from < 0 ? this.length + from : from; - return this.push.apply(this, rest); +define('constants',['require'],function ( require ) { + + return { + TAU: 2 * Math.PI, + PI: Math.PI + }; + +}); +define('equal',['require'],function ( require ) { + + function equal( arg1, arg2, e ) { + e = e || 0.000001; + + return Math.abs( arg1 - arg2 ) < e; + } + + return equal; + +}); +define('vector/v',['require'],function ( require ) { + + var V = function() { + }; + + return V; + +}); +define('vector/v2',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V2 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 2 ); + + for( i = 0; i < argc && vi < 2; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 2; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 2; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V2.prototype = new V(); + V2.prototype.constructor = V2; + + return V2; + + }; + +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + + + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } + + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } + + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, + + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, + + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, + + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; + + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; + + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; + + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; + + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; + + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; + + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; + + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } + + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } + + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); + + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } + + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } + + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); + + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); + + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); + + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); + + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); + + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); + + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } + + var length = collection.length, + noaccum = arguments.length < 3; + + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; + + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } + + var prop, + props = keys(collection); + + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } + + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); + + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); + + + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); + + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); + + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } + + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } + + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } + + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } + + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } + + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } + + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } + + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } + + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } + + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); + + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } + + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } + + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } + + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; + } + + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } + + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } + + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } + + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } + + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } + + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } + + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; + + return function() { + var result, + others = arguments; + + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } + + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } + + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { + return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } + + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; + + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } + + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } + + var index = -1, + result = true, + size = 0; + + // add the first collection to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } + + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } + + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } + + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; + + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } + + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } + + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } + + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } + + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } + + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; + + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; + + // clear stored code snippets + tokenized.length = 0; + + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; + + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } + + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } + + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } + + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } + + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } + + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } + + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } + + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; + }; + }); + + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); + + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('vector/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); + +define('common/not-implemented',['require'],function ( require ) { + + return function notImplemented() { + throw new Error( "not implemented" ); + }; + +}); +define('vector/vector',['require'],function ( require ) { + + var Vector = function() { + }; + + return Vector; + +}); +define('vector/vector2-api',['require','common/not-implemented','vector/v2'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + + return result; + } + + function angle( v1, v2 ) { + var normalizedV1 = new V2(); + var normalizedV2 = new V2(); + + normalize(v1, normalizedV1); + normalize(v2, normalizedV2); + + return Math.acos( dot( normalizedV1, normalizedV2 ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + + return v; + } + + function dot( v1, v2 ) { + var r = 0; + + r += v1[0] * v2[0]; + r += v1[1] * v2[1]; + + return r; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V2(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + + return result; + } + + function negate( v, result ) { + result = result || new V2(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + + return result; + } + + function normalize( v, result ) { + result = result || new V2(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + + return result; + } + + function project( v1, v2, result ) { + result = result || new V2(); + + var dp = v1[0]*v2[0] + v1[1]*v2[1]; + var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); + + result[0] = dp_over_v2_squared_length * v2[0]; + result[1] = dp_over_v2_squared_length * v2[1]; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + + return result; + } + + var vector2 = { + add: add, + angle: angle, + clear: clear, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract, + + x: new V2( 1, 0 ), + u: new V2( 1, 0 ), + y: new V2( 0, 1 ), + v: new V2( 0, 1 ), + zero: new V2( 0, 0 ), + one: new V2( 1, 1 ) + }; + + return vector2; + + }; + +}); +define('vector/vector2',['require','../../lib/lodash','common/not-implemented','vector/v2','vector/vector2-api','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); + var vector2 = require( "vector/vector2-api" )( FLOAT_ARRAY_TYPE ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector2 = function( arg1, arg2 ) { + var argc = arguments.length; + + this.buffer = new V2( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); + + this.modified = true; + this.size = 2; + }; + Vector2.prototype = new Vector(); + Vector2.prototype.constructor = Vector2; + + function add( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.angle( this.buffer, other ); + } + + function clear() { + vector2.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector2( this ); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.equal( this.buffer, other ); + } + + function length() { + return vector2.length( this.buffer ); + } + + function multiply( arg, result ) { + result = result || this; + vector2.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector2.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector2.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function project( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.project( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector2 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + this.modified = true; + } + } else if( 2 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector2.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract + }); + + return Vector2; + + }; + +}); +define('vector/v3',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V3 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 3 ); + + for( i = 0; i < argc && vi < 3; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 3; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 3; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V3.prototype = new V(); + V3.prototype.constructor = V3; + + return V3; + + }; + +}); +define('matrix/matrix',['require'],function ( require ) { + + var Matrix = function() { + }; + + return Matrix; + +}); +define('vector/vector3-api',['require','common/not-implemented','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + return; + } + + if( undefined === result ) { + result = new V3( v1[0] + v2[0], + v1[1] + v2[1], v1[2] + v2[2] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + + return v; + } + + function cross( v1, v2, result ) { + result = result || new V3(); + + var v1_0 = v1[0], + v1_1 = v1[1], + v1_2 = v1[2]; + var v2_0 = v2[0], + v2_1 = v2[1], + v2_2 = v2[2]; + + result[0] = (v1_1 * v2_2) - (v2_1 * v1_2); + result[1] = (v1_2 * v2_0) - (v2_2 * v1_0); + result[2] = (v1_0 * v2_1) - (v2_0 * v1_1); + + return result; + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V3(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + + return result; + } + + function negate( v, result ) { + result = result || new V3(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + + return result; + } + + function normalize( v, result ) { + result = result || new V3(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V3(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + + return result; + } + + //This does a matrix3 by vector3 transform, which is a matrix multiplication + //The matrix3 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V3(); + + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[1] * y + m[2] * z; + result[1] = m[3] * x + m[4] * y + m[5] * z; + result[2] = m[6] * x + m[7] * y + m[8] * z; + + return result; + } + + var vector3 = { + add: add, + angle: angle, + clear: clear, + cross: cross, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V3( 1, 0, 0 ), + y: new V3( 0, 1, 0 ), + z: new V3( 0, 0, 1 ), + zero: new V3( 0, 0, 0 ), + one: new V3( 1, 1, 1 ) + }; + + return vector3; + + }; + +}); +define('vector/vector3',['require','../../lib/lodash','common/not-implemented','vector/v3','vector/vector3-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + var vector3 = require( "vector/vector3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector3 = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + + this.buffer = new V3( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + + this.modified = true; + this.size = 3; + }; + Vector3.prototype = new Vector(); + Vector3.prototype.constructor = Vector3; + + function add( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.angle( this.buffer, other ); + } + + function clear() { + vector3.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector3( this ); + } + + function cross( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.cross( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function dot( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.equal( this.buffer, other ); + } + + function length() { + return vector3.length( this.buffer ); + } + + function multiply( arg, result ) { + result = result || this; + vector3.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector3.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector3.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector3 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + this.modified = true; + } + } else if( 3 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector3.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + cross: cross, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector3; + + }; + +}); +define('vector/v4',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V4 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 4 ); + + for( i = 0; i < argc && vi < 4; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 4; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 4; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V4.prototype = new V(); + V4.prototype.constructor = V4; + + return V4; + + }; + +}); +define('vector/vector4-api',['require','common/not-implemented','vector/v4'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + v1[3] += v2[3]; + return; + } + + if( undefined === result ) { + result = new V4( v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + result[3] = v1[3] + v2[3]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + v[3] = 0; + + return v; + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + var d3 = Math.abs( v1[3] - v2[3] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + r += v[2] * v[2]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V4(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + result[3] = s * v[3]; + + return result; + } + + function negate( v, result ) { + result = result || new V4(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + result[3] = -1 * v[3]; + + return result; + } + + function normalize( v, result ) { + result = result || new V4(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + result[3] = v[3]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + v[3] = arguments[1][3]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + v[3] = arguments[4]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V4(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + result[3] = v1[3] - v2[3]; + + return result; + } + + //This does a matrix4 by vector4 transform, which is a matrix multiplication + //The matrix4 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V4(); + + var x = v[0], y = v[1], z = v[2], w = v[3]; + + result[0] = m[0] * x + m[1] * y + m[2] * z + m[3] * w; + result[1] = m[4] * x + m[5] * y + m[6] * z + m[7] * w; + result[2] = m[8] * x + m[9] * y + m[10] * z + m[11] * w; + result[3] = m[12] * x + m[13] * y + m[14] * z + m[15] * w; + + return result; + } + + var vector4 = { + add: add, + angle: angle, + clear: clear, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V4( 1, 0, 0, 0 ), + y: new V4( 0, 1, 0, 0 ), + z: new V4( 0, 0, 1, 0 ), + w: new V4( 0, 0, 0, 1 ), + zero: new V4( 0, 0, 0, 0 ), + one: new V4( 1, 1, 1, 1 ) + }; + + return vector4; + + }; + +}); +define('vector/vector4',['require','../../lib/lodash','common/not-implemented','vector/v4','vector/vector4-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + var vector4 = require( "vector/vector4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector4 = function( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + + this.buffer = new V4( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3, + (arg4 instanceof Vector) ? arg4.buffer : arg4 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + w: { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + + this.modified = true; + this.size = 4; + }; + Vector4.prototype = new Vector(); + Vector4.prototype.constructor = Vector4; + + function add( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.angle( this.buffer, other ); + } + + function clear() { + vector4.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector4( this ); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.equal( this.buffer, other ); + } + + function length() { + return vector4.length( this.buffer ); + } + + function multiply( arg, result ) { + result = result || this; + vector4.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector4.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector4.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector4 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + buffer[3] = arg1[3]; + this.modified = true; + } + } else if( 4 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector4.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector4; + + }; + +}); +define('matrix/m',['require'],function ( require ) { + + var M = function() { + }; + + return M; + +}); +define('matrix/m2',['require','matrix/m'],function ( require ) { + + var M = require( "matrix/m" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var M2 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, + 0, 0]; + } else { + elements = arguments; + } + + var matrix = new FLOAT_ARRAY_TYPE( 4 ); + for( var i = 0; i < 4; ++ i ) { + matrix[i] = elements[i]; + } + + return matrix; + }; + M2.prototype = new M(); + M2.prototype.constructor = M2; + + return M2; + + }; + +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + + + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } + + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } + + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, + + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, + + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, + + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; + + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; + + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; + + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; + + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; + + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; + + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; + + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } + + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } + + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); + + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } + + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } + + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); + + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); + + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); + + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); + + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); + + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); + + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } + + var length = collection.length, + noaccum = arguments.length < 3; + + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; + + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } + + var prop, + props = keys(collection); + + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } + + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); + + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); + + + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); + + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); + + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } + + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } + + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } + + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } + + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } + + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } + + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } + + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } + + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } + + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); + + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } + + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } }; -} + } -define('lang',['require'],function ( require ) { + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } - return { - // Simple bind function to maintain "this" for a function. - bind: function bind( obj, func ) { - return function() { - return func.apply( obj, arguments ); - }; - }, + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } - extend: function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } //if - } //for - } //extend + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; }; -}); + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } -define('constants',['require'],function ( require ) { + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } + + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } + + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } + + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } + + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } + + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; + + return function() { + var result, + others = arguments; + + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } + + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } + + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } + + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; + + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } + + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } + + var index = -1, + result = true, + size = 0; + + // add the first collection to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } + + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } + + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } + + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; + + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } + + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } + + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } + + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } + + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } + + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; + + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; + + // clear stored code snippets + tokenized.length = 0; + + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; + + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } + + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } + + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } + + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } + + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } + + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } + + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } + + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; + }; + }); + + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); + + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('matrix/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); + +define('matrix/matrix2-api',['require','common/not-implemented','matrix/m2'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M2(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + + return result; + } + + function clear( m ) { + m[0] = m[1] = 0; + m[2] = m[3] = 0; + + return m; + } + + function determinant( m ) { + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3]; + + return a00 * a11 - a01 * a10; + } + + function equal( m1, m2, e ) { + e = e || 0.000001; + + if( m1.length !== m2.length ) { + return false; + } + + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } + + return true; + } + + function inverse( m, result ) { + result = result || new M2(); + + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3], + + determinant = a00 * a11 - a01 * a10, + inverseDeterminant; + + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; + + result[0] = a11 * inverseDeterminant; + result[1] = -a01 * inverseDeterminant; + result[2] = -a10 * inverseDeterminant; + result[3] = a00 * inverseDeterminant; + + return result; + } + + function multiply( m1, m2, result ) { + result = result || new M2(); - var constants = { - - TAU: 2 * Math.PI, - - PI: Math.PI, - - HALF_PI: Math.PI / 2.0 - - }; - - return constants; - + var a00 = m1[0], a01 = m1[1], a10 = m1[2], a11 = m1[3]; + var b00 = m2[0], b01 = m2[1], b10 = m2[2], b11 = m2[3]; + + result[0] = a00 * b00 + a01 * b10; + result[1] = a00 * b01 + a01 * b11; + result[2] = a10 * b00 + a11 * b10; + result[3] = a10 * b01 + a11 * b11; + + return result; + } + + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + } + + return m; + } + + function subtract( m1, m2, result ) { + result = result || new M2(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + + return result; + } + + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1]; + + result[1] = m[2]; + result[2] = a01; + + return result; + } + + result = result || new M2(); + + result[0] = m[0]; + result[1] = m[2]; + result[2] = m[1]; + result[3] = m[3]; + + return result; + } + + var matrix2 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M2( 0, 0, + 0, 0 ), + one: new M2( 1, 1, + 1, 1 ), + identity: new M2( 1, 0, + 0, 1 ) }; + return matrix2; + + }; + }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/matrix2',['require','../../lib/lodash','common/not-implemented','matrix/m2','matrix/matrix2-api','matrix/matrix'],function ( require ) { -define('vector/vector',['require'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); + var matrix2 = require( "matrix/matrix2-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } - return function( FLOAT_ARRAY_TYPE ) { + function getValue( index ) { + return this.buffer[index]; + } - var Vector = function( dim, args ) { + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } + function updateViews() { + var i; + for( i = 0; i < 2; ++ i ) { + this._views[i] = new Matrix2View( this, this.buffer, + i*2, (i+1)*2 ); + } + } - var vector = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - vector[i] = elements[i]; - } + var Matrix2View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - return vector; + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); + }; - }; + var Matrix2 = function( arg1, arg2, + arg3, arg4 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + this.buffer = new M2( arg1.buffer ); + } else { + this.buffer = new M2( arg1 ); + } + } else if( 4 === argc ) { + this.buffer = new M2( arg1, arg2, + arg3, arg4 ); + } else { + this.buffer = new M2(); + } - var vector = { - - $: Vector, + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + } + }); - add: function( v1, v2, result ) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] += v1[i] + v2[i]; - } + this._views = []; - return result; - }, + updateViews.call( this ); - clear: function( v ) { - for( var i = 0; i < v.length; ++ i ) { - v[i] = 0; - } - }, + this.modified = true; + }; + Matrix2.prototype = new Matrix(); + Matrix2.prototype.constructor = Matrix2; - dot: function( v1, v2 ) { - var res = 0; - for( var i = 0; i < v1.length; ++ i) { - res += v1[i] * v2[i]; - } - return res; - }, + function add( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - equal: function( v1, v2, e ) { - e = e || 0.000001; + result = result || this; + matrix2.add( this.buffer, other, result.buffer ); + result.modified = true; - if( v1.length != v2.length ) { - return false; - } + return this; + } - var dim = v1.length; - for( var i = 0; i < dim; ++ i ) { - if ( Math.abs(v1[i] - v2[i]) > e ) { - return false; - } - } + function clear() { + matrix2.clear( this.buffer ); + this.modified = true; - return true; - }, + return this; + } - length: function( v ) { - var va = 0; - for( var i = 0; i < v.length; ++ i ) { - va += v[i] * v[i]; - } + function clone() { + return new Matrix2( this ); + } - return Math.sqrt(va); - }, + function determinant() { + return matrix2.determinant( this.buffer ); + } - multiply: function( v, s, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * s; - } + function equal( arg ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - return result; - }, - - negate: function( v, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * -1; - } - - return result; - }, - - normalize: function( v, result ) { - var len = v.length; - for( var i = 0, abslen = vector.length(v); i < len; ++ i ) { - result[i] = v[i] / abslen; - } + return matrix2.equal( this.buffer, other ); + } - return result; - }, + function inverse( result ) { + result = result || this; + if( !matrix2.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix2.inverse( this.buffer, result.buffer ); + result.modified = true; - subtract: function( v1, v2, result) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] = v1[i] - v2[i]; - } + return this; + } - return result; - } + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - }; - - Object.defineProperty( vector, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + result = result || this; + matrix2.multiply( this.buffer, other, result.buffer ); + result.modified = true; - Object.defineProperty( vector, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + return this; + } - Object.defineProperty( vector, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } - Object.defineProperty( vector, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - Object.defineProperty( vector, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + result = result || this; + matrix2.subtract( this.buffer, other, result.buffer ); + result.modified = true; - Object.defineProperty( vector, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + return this; + } - return vector; + function transpose( result ) { + result = result || this; + matrix2.transpose( this.buffer, result.buffer ); + result.modified = true; - }; + return this; + } + + _.extend( Matrix2.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix2; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/m3',['require','matrix/m'],function ( require ) { -define('vector/vector2',['require','./vector','../constants'],function ( require ) { + var M = require( "matrix/m" ); - return function( FLOAT_ARRAY_TYPE ) { + return function( FLOAT_ARRAY_TYPE ) { - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); - var constants = require( '../constants' )(); + var M3 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, + 0, 0, 0, + 0, 0, 0]; + } else { + elements = arguments; + } - var Vector2 = function() { - if( 0 === arguments.length ) { - return vector.$( 2, [0, 0] ); - } else { - return vector.$( 2, arguments ); - } - }; - - var vector2 = { - - $: Vector2, - - add: function( v1, v2, result ) { - result = result || Vector2(); + var matrix = new FLOAT_ARRAY_TYPE( 9 ); + for( var i = 0; i < 9; ++ i ) { + matrix[i] = elements[i]; + } - return vector.add( v1, v2, result ); - }, + return matrix; + }; + M3.prototype = new M(); + M3.prototype.constructor = M3; - angle: function( v1, v2 ) { - var nV1 = Vector2(); - var nV2 = Vector2(); + return M3; - vector.normalize(v1, nV1); - vector.normalize(v2, nV2); + }; - return Math.acos( vector.dot( nV1, nV2 ) ); - }, +}); +define('matrix/matrix3-api',['require','common/not-implemented','matrix/m3'],function ( require ) { - clear: vector.clear, + return function( FLOAT_ARRAY_TYPE ) { - dot: vector.dot, + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); - equal: vector.equal, + function add( m1, m2, result ) { + result = result || new M3(); - length: vector.length, + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; - multiply: function( v, s, result ) { - result = result || Vector2(); + return result; + } - return vector.multiply( v, s, result ); - }, - - negate: function( v, result ) { - result = result || Vector2(); - - return vector.negate( v, result ); - }, + function clear( m ) { + m[0] = m[1] = m[2] = 0; + m[3] = m[4] = m[5] = 0; + m[6] = m[7] = m[8] = 0; - normalize: function( v, result ) { - result = result || Vector2(); - var len = vector.length(v); + return m; + } - result[0] = v[0]/len; - result[1] = v[1]/len; + function determinant( m ) { + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8]; - return result; - }, - - project: function( v1, v2, result ) { - result = result || Vector2(); - - var dp = v1[0]*v2[0] + v1[1]*v2[1]; - var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); - - result[0] = dp_over_v2_squared_length * v2[0]; - result[1] = dp_over_v2_squared_length * v2[1]; - - return result; - }, - - set: function( v, x, y ) { - v[0] = x; - v[1] = y; - }, - - subtract: function( v1, v2, result ) { - result = result || Vector2(); + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + } - return vector.subtract( v1, v2, result ); - } - - }; - - Object.defineProperty( vector2, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + function equal( m1, m2, e ) { + e = e || 0.000001; - Object.defineProperty( vector2, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + if( m1.length !== m2.length ) { + return false; + } - Object.defineProperty( vector2, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e ) { + return false; + } - Object.defineProperty( vector2, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + return true; + } - Object.defineProperty( vector2, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + function inverse( m, result ) { + result = result || new M3(); - Object.defineProperty( vector2, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8], - return vector2; + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, - }; + determinant = a00 * b01 + a01 * b11 + a02 * b21, + inverseDeterminant; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; -define('vector/vector3',['require','./vector'],function ( require ) { + result[0] = b01 * inverseDeterminant; + result[1] = (-a22 * a01 + a02 * a21) * inverseDeterminant; + result[2] = (a12 * a01 - a02 * a11) * inverseDeterminant; + result[3] = b11 * inverseDeterminant; + result[4] = (a22 * a00 - a02 * a20) * inverseDeterminant; + result[5] = (-a12 * a00 + a02 * a10) * inverseDeterminant; + result[6] = b21 * inverseDeterminant; + result[7] = (-a21 * a00 + a01 * a20) * inverseDeterminant; + result[8] = (a11 * a00 - a01 * a10) * inverseDeterminant; - return function( FLOAT_ARRAY_TYPE ) { + return result; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L682 + function multiply( m1, m2, result ) { + result = result || new M3(); + + // Cache the matrix values (makes for huge speed increases!) + var a00 = m1[0], a01 = m1[1], a02 = m1[2], + a10 = m1[3], a11 = m1[4], a12 = m1[5], + a20 = m1[6], a21 = m1[7], a22 = m1[8], - var Vector3 = function() { - if( 0 === arguments.length ) { - return vector.$( 3, [0, 0, 0] ); - } else { - return vector.$( 3, arguments ); - } - }; + b00 = m2[0], b01 = m2[1], b02 = m2[2], + b10 = m2[3], b11 = m2[4], b12 = m2[5], + b20 = m2[6], b21 = m2[7], b22 = m2[8]; - var vector3 = { - - $: Vector3, + result[0] = a00 * b00 + a01 * b10 + a02 * b20; + result[1] = a00 * b01 + a01 * b11 + a02 * b21; + result[2] = a00 * b02 + a01 * b12 + a02 * b22; - add: function( v1, v2, result ) { - result = result || Vector3(); + result[3] = a10 * b00 + a11 * b10 + a12 * b20; + result[4] = a10 * b01 + a11 * b11 + a12 * b21; + result[5] = a10 * b02 + a11 * b12 + a12 * b22; - return vector.add( v1, v2, result ); - }, + result[6] = a20 * b00 + a21 * b10 + a22 * b20; + result[7] = a20 * b01 + a21 * b11 + a22 * b21; + result[8] = a20 * b02 + a21 * b12 + a22 * a22; - angle: function( v1, v2 ) { + return result; + } - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2])) - ); - }, + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + } + + return m; + } - clear: vector.clear, + function subtract( m1, m2, result ) { + result = result || new M3(); - cross: function( v1, v2, result ) { - result = result || Vector3(); + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; - result[0] = (v1[1] * v2[2]) - (v2[1] * v1[2]); - result[1] = (v1[2] * v2[0]) - (v2[2] * v1[0]); - result[2] = (v1[0] * v2[1]) - (v2[0] * v1[1]); + return result; + } - return result; - }, + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], + a12 = m[5]; - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - }, + result[1] = m[3]; + result[2] = m[6]; + result[3] = a01; + result[5] = m[7]; + result[6] = a02; + result[7] = a12; - equal: vector.equal, + return result; + } - length: vector.length, + result = result || new M3(); - multiply: function( v, s, result ) { - result = result || Vector3(); + result[0] = m[0]; + result[1] = m[3]; + result[2] = m[6]; + result[3] = m[1]; + result[4] = m[4]; + result[5] = m[7]; + result[6] = m[2]; + result[7] = m[5]; + result[8] = m[8]; - return vector.multiply( v, s, result ); - }, + return result; + } - normal: function( v1, v2, result ) { - result = result || Vector3(); + var matrix3 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M3( 0, 0, 0, + 0, 0, 0, + 0, 0, 0 ), + one: new M3( 1, 1, 1, + 1, 1, 1, + 1, 1, 1 ), + identity: new M3( 1, 0, 0, + 0, 1, 0, + 0, 0, 1 ) + }; + + return matrix3; - return Vector3.cross( v1, v2, result ); - }, + }; - normalize: function( v, result ) { - result = result || Vector3(); - var len = vector.length(v); +}); +define('matrix/matrix3',['require','../../lib/lodash','common/not-implemented','matrix/m3','matrix/matrix3-api','matrix/matrix'],function ( require ) { - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); + var matrix3 = require( "matrix/matrix3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } - return result; - }, - - set: function( v, x, y, z ) { - v[0] = x; - v[1] = y; - v[2] = z; - }, + function getValue( index ) { + return this.buffer[index]; + } - subtract: function( v1, v2, result ) { - result = result || Vector3(); + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - return vector.subtract( v1, v2, result ); - } + function updateViews() { + var i; + for( i = 0; i < 3; ++ i ) { + this._views[i] = new Matrix3View( this, this.buffer, + i*3, (i+1)*3 ); + } + } - }; - - Object.defineProperty( vector3, 'x', { - get: function() { - return Vector3( [1, 0, 0] ); - }, - enumerable: true - }); + var Matrix3View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - Object.defineProperty( vector3, 'y', { - get: function() { - return Vector3( [0, 1, 0] ); - }, - enumerable: true - }); + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + }; - Object.defineProperty( vector3, 'z', { - get: function() { - return Vector3( [0, 0, 1] ); - }, - enumerable: true - }); + var Matrix3 = function( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + this.buffer = new M3( arg1.buffer ); + } else { + this.buffer = new M3( arg1 ); + } + } else if( 9 === argc ) { + this.buffer = new M3( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ); + } else { + this.buffer = new M3(); + } - Object.defineProperty( vector3, 'zero', { - get: function() { - return Vector3( [0, 0, 0] ); - }, - enumerable: true - }); + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + } + }); - Object.defineProperty( vector3, 'one', { - get: function() { - return Vector3( [1, 1, 1] ); - }, - enumerable: true - }); + this._views = []; - return vector3; + updateViews.call( this ); + this.modified = true; }; + Matrix3.prototype = new Matrix(); + Matrix3.prototype.constructor = Matrix3; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function add( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } -define('vector/vector4',['require','./vector'],function ( require ) { + result = result || this; + matrix3.add( this.buffer, other, result.buffer ); + result.modified = true; - return function( FLOAT_ARRAY_TYPE ) { + return this; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + function clear() { + matrix3.clear( this.buffer ); + this.modified = true; - var Vector4 = function() { - if( 0 === arguments.length ) { - return vector.$( 4, [0, 0, 0, 0] ); - } else { - return vector.$( 4, arguments ); - } - }; + return this; + } - var vector4 = { - - $: Vector4, + function clone() { + return new Matrix3( this ); + } - add: function( v1, v2, result ) { - result = result || Vector4(); + function determinant() { + return matrix3.determinant( this.buffer ); + } - result[0] = v1[0] + v2[0]; - result[1] = v1[1] + v2[1]; - result[2] = v1[2] + v2[2]; - result[3] = v1[3] + v2[3]; + function equal( arg ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - return result; - }, + return matrix3.equal( this.buffer, other ); + } - // Computes the angle between v1 and v2 - angle: function( v1, v2 ) { - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3])) - ); - }, + function inverse( result ) { + result = result || this; + if( !matrix3.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix3.inverse( this.buffer, result.buffer ); + result.modified = true; - clear: vector.clear, + return this; + } - // Computes the dot product of v1 and v2 - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; - }, + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - equal: vector.equal, + result = result || this; + matrix3.multiply( this.buffer, other, result.buffer ); + result.modified = true; - length: vector.length, + return this; + } - // Computes v * s - multiply: function( v, s, result ) { - result = result || Vector4(); + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } - return vector.multiply( v, s, result ); - }, + return this; + } - // Computes a Vector4 with same direction as v having unit length - normalize: function( v, result ) { - result = result || Vector4(); - var len = vector.length(v); + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; - result[3] = v[3]/len; + result = result || this; + matrix3.subtract( this.buffer, other, result.buffer ); + result.modified = true; - return result; - }, - - set: function( v, x, y, z, w ) { - v[0] = x; - v[1] = y; - v[2] = z; - v[3] = w; - }, - - // Computes v1 - v2 - subtract: function( v1, v2, result ) { - result = result || Vector4(); - - return vector.subtract( v1, v2, result ); - } + return this; + } - } - - Object.defineProperty( vector4, 'x', { - get: function() { - return Vector4( [1, 0, 0, 0] ); - }, - enumerable: true - }); + function transpose( result ) { + result = result || this; + matrix3.transpose( this.buffer, result.buffer ); + result.modified = true; - Object.defineProperty( vector4, 'y', { - get: function() { - return Vector4( [0, 1, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'z', { - get: function() { - return Vector4( [0, 0, 1, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'w', { - get: function() { - return Vector4( [0, 0, 0, 1] ); - }, - enumerable: true - }); + return this; + } + + _.extend( Matrix3.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); - Object.defineProperty( vector4, 'zero', { - get: function() { - return Vector4( [0, 0, 0, 0] ); - }, - enumerable: true - }); + return Matrix3; - Object.defineProperty( vector4, 'one', { - get: function() { - return Vector4( [1, 1, 1, 1] ); - }, - enumerable: true - }); + }; + +}); +define('matrix/m4',['require','matrix/m'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var M = require( "matrix/m" ); + + var M4 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0]; + } else { + elements = arguments; + } - return vector4; + var matrix = new FLOAT_ARRAY_TYPE( 16 ); + for( var i = 0; i < 16; ++ i ) { + matrix[i] = elements[i]; + } + return matrix; }; + M4.prototype = new M(); + M4.prototype.constructor = M4; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + return M4; -define('vector/quaternion',['require','./vector4','./vector3'],function ( require ) { + }; + +}); +define('matrix/matrix4-api',['require','common/not-implemented','matrix/m4','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; + result[9] = m1[9] + m2[9]; + result[10] = m1[10] + m2[10]; + result[11] = m1[11] + m2[11]; + result[12] = m1[12] + m2[12]; + result[13] = m1[13] + m2[13]; + result[14] = m1[14] + m2[14]; + result[15] = m1[15] + m2[15]; + + return result; + } - return function( FLOAT_ARRAY_TYPE ) { + function clear( m ) { + m[0] = m[1] = m[2] = m[3] = 0; + m[4] = m[5] = m[6] = m[7] = 0; + m[8] = m[9] = m[10] = m[11] = 0; + m[12] = m[13] = m[14] = m[15] = 0; - var vector4 = require( './vector4' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( './vector3' )( FLOAT_ARRAY_TYPE ); + return m; + } - var Quaternion = vector4.$; + function determinant( m ) { + var a0 = m[0] * m[5] - m[1] * m[4]; + var a1 = m[0] * m[6] - m[2] * m[4]; + var a2 = m[0] * m[7] - m[3] * m[4]; + var a3 = m[1] * m[6] - m[2] * m[5]; + var a4 = m[1] * m[7] - m[3] * m[5]; + var a5 = m[2] * m[7] - m[3] * m[6]; + var b0 = m[8] * m[13] - m[9] * m[12]; + var b1 = m[8] * m[14] - m[10] * m[12]; + var b2 = m[8] * m[15] - m[11] * m[12]; + var b3 = m[9] * m[14] - m[10] * m[13]; + var b4 = m[9] * m[15] - m[11] * m[13]; + var b5 = m[10] * m[15] - m[11] * m[14]; + + return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; + } - var quaternion = { + function equal( m1, m2, e ) { + e = e || 0.000001; - $: Quaternion, + if( m1.length !== m2.length ) { + return false; + } - to: { - rpy: function( q, result ) { - var r = result || vector3.$(); - var atan2 = Math.atan2, - asin = Math.asin; + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + var d9 = Math.abs( m1[9] - m2[9] ); + var d10 = Math.abs( m1[10] - m2[10] ); + var d11 = Math.abs( m1[11] - m2[11] ); + var d12 = Math.abs( m1[12] - m2[12] ); + var d13 = Math.abs( m1[13] - m2[13] ); + var d14 = Math.abs( m1[14] - m2[14] ); + var d15 = Math.abs( m1[15] - m2[15] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e || + isNaN( d9 ) || d9 > e || + isNaN( d10 ) || d10 > e || + isNaN( d11 ) || d11 > e || + isNaN( d12 ) || d12 > e || + isNaN( d13 ) || d13 > e || + isNaN( d14 ) || d14 > e || + isNaN( d15 ) || d15 > e ) { + return false; + } - r[0] = atan2( 2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] + 2*q[2]*q[2] ); - r[1] = asin( 2*q[0]*q[2] - 2*q[3]*q[1] ); - r[2] = atan2( 2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] + 2*q[3]*q[3] ); + return true; + } - if( !result ) { - return r; - } - } - }, - - from: { - rpy: function( v, result ) { - var r = result || quaternion.$(); - var sin = Math.sin, - cos = Math.cos; - var half_phi = v[0] / 2, - half_theta = v[1] / 2, - half_psi = v[2] / 2; - var sin_half_phi = sin( half_phi ), - cos_half_phi = cos( half_phi ), - sin_half_theta = sin( half_theta ), - cos_half_theta = cos( half_theta ), - sin_half_psi = sin( half_psi ), - cos_half_psi = cos( half_psi ); - - r[0] = cos_half_phi * cos_half_theta * cos_half_psi + - sin_half_phi * sin_half_theta * sin_half_psi; - r[1] = sin_half_phi * cos_half_theta * cos_half_psi - - cos_half_phi * sin_half_theta * sin_half_psi; - r[2] = cos_half_phi * sin_half_theta * cos_half_psi + - sin_half_phi * cos_half_theta * sin_half_psi; - r[3] = cos_half_phi * cos_half_theta * sin_half_psi - - sin_half_phi * sin_half_theta * cos_half_psi; - - if( !result ) { - return r; - } - } - }, + function inverse( m, result ) { + result = result || new M4(); + + var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], + a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], + a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], + a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + determinant = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), + inverseDeterminant; + + // Determinant, throw exception if singular + if( !determinant ) { + return undefined; + } + + inverseDeterminant = 1 / determinant; + + result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * inverseDeterminant; + result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * inverseDeterminant; + result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * inverseDeterminant; + result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * inverseDeterminant; + result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * inverseDeterminant; + result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * inverseDeterminant; + result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * inverseDeterminant; + result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * inverseDeterminant; + result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * inverseDeterminant; + result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * inverseDeterminant; + result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * inverseDeterminant; + result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * inverseDeterminant; + result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * inverseDeterminant; + result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * inverseDeterminant; + result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * inverseDeterminant; + result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * inverseDeterminant; + + return result; + } - length: vector4.length, + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L1295 + function multiply( m1, m2, result ) { + result = result || new M4(); + + var a00 = m1[0], a01 = m1[1], a02 = m1[2], a03 = m1[3], + a10 = m1[4], a11 = m1[5], a12 = m1[6], a13 = m1[7], + a20 = m1[8], a21 = m1[9], a22 = m1[10], a23 = m1[11], + a30 = m1[12], a31 = m1[13], a32 = m1[14], a33 = m1[15], + + b00 = m2[0], b01 = m2[1], b02 = m2[2], b03 = m2[3], + b10 = m2[4], b11 = m2[5], b12 = m2[6], b13 = m2[7], + b20 = m2[8], b21 = m2[9], b22 = m2[10], b23 = m2[11], + b30 = m2[12], b31 = m2[13], b32 = m2[14], b33 = m2[15]; + + result[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; + result[1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; + result[2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; + result[3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; + result[4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; + result[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; + result[6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; + result[7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; + result[8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; + result[9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; + result[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; + result[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; + result[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; + result[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; + result[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; + result[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; + + return result; + } - multiply: function( q1, q2, result ) { - var r = result || quaternion.$(); + function multiplyV3( m, v, result ) { + result = result || new V3(); - r[0] = q1[3] * q2[0] + q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1]; // x - r[1] = q1[3] * q2[1] - q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0]; // y - r[2] = q1[3] * q2[2] + q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3]; // z - r[3] = q1[3] * q2[3] - q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2]; // w + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + result[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + result[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - if( !result ) { - return r; - } - }, + return result; + } - normalize: vector4.normalize + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + m[9] = values[9]; + m[10] = values[10]; + m[11] = values[11]; + m[12] = values[12]; + m[13] = values[13]; + m[14] = values[14]; + m[15] = values[15]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + m[9] = arguments[10]; + m[10] = arguments[11]; + m[11] = arguments[12]; + m[12] = arguments[13]; + m[13] = arguments[14]; + m[14] = arguments[15]; + m[15] = arguments[16]; + } + + return m; + } - }; + function subtract( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; + result[9] = m1[9] - m2[9]; + result[10] = m1[10] - m2[10]; + result[11] = m1[11] - m2[11]; + result[12] = m1[12] - m2[12]; + result[13] = m1[13] - m2[13]; + result[14] = m1[14] - m2[14]; + result[15] = m1[15] - m2[15]; + + return result; + } - Object.defineProperty( quaternion, 'identity', { - get: function() { - return Quaternion( [0, 0, 0, 1] ); - }, - enumerable: true - }); + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], a03 = m[3], + a12 = m[6], a13 = m[7], + a23 = m[11]; + + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = a01; + result[6] = m[9]; + result[7] = m[13]; + result[8] = a02; + result[9] = a12; + result[11] = m[14]; + result[12] = a03; + result[13] = a13; + result[14] = a23; + + return result; + } - return quaternion; + result = result || new M4(); + + result[0] = m[0]; + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = m[1]; + result[5] = m[5]; + result[6] = m[9]; + result[7] = m[13]; + result[8] = m[2]; + result[9] = m[6]; + result[10] = m[10]; + result[11] = m[14]; + result[12] = m[3]; + result[13] = m[7]; + result[14] = m[11]; + result[15] = m[15]; + + return result; + } + var matrix4 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M4( 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 ), + one: new M4( 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1 ), + identity: new M4( 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ) }; + + return matrix4; + + }; }); +define('matrix/matrix4',['require','../../lib/lodash','common/not-implemented','matrix/m4','matrix/matrix4-api','matrix/matrix'],function ( require ) { -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } -define('matrix/matrix',['require'],function ( require ) { + function getValue( index ) { + return this.buffer[index]; + } - return function( FLOAT_ARRAY_TYPE ) { - - var Matrix = function( dim, args ) { - - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } - - var matrix = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - matrix[i] = elements[i]; - } - - return matrix; - - }; - - var matrix = { - - $: Matrix, - - add: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - result[i] += m1[i] + m2[i]; - } + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - return result; - }, - - subtract: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - m1[i] -= m2[i]; - } - return m1; - }, - - clear: function( m ) { - for( var i = 0; i < m.length; ++ i ) { - m[i] = 0; - } - }, - - equal: function( m1, m2, e ) { - e = e || 0.000001; + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new Matrix4View( this, this.buffer, + i*4, (i+1)*4 ); + } + } - if( m1.length != m2.length ) { - return false; - } - - var dim = m1.length; - for( var i = 0; i < dim; ++ i ) { - if( Math.abs( m1[i] - m2[i] ) > e ) { - return false; - } - } + var Matrix4View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - return true; - } - - }; - - return matrix; - + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); }; - -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var Matrix4 = function( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else { + this.buffer = new M4( arg1 ); + } + } else if( 16 === argc ) { + this.buffer = new M4( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ); + } else { + this.buffer = new M4(); + } -define('matrix/matrix2',['require','./matrix'],function ( require ) { + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); - return function( FLOAT_ARRAY_TYPE ) { + this._views = []; - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + updateViews.call( this ); - var Matrix2 = function() { - if( 0 === arguments.length ) { - return matrix.$( 4, [0, 0, - 0, 0] ); - } else { - return matrix.$( 4, arguments ); - } - }; + this.modified = true; + }; + Matrix4.prototype = new Matrix(); + Matrix4.prototype.constructor = Matrix4; - var matrix2 = { + function add( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - $: Matrix2, + result = result || this; + matrix4.add( this.buffer, other, result.buffer ); + result.modified = true; - add: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + return this; + } - subtract: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) { - result = temp; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + function clear() { + matrix4.clear( this.buffer ); + this.modified = true; + + return this; + } - clear: matrix.clear, + function clone() { + return new Matrix4( this ); + } - equal: matrix.equal, + function determinant() { + return matrix4.determinant( this.buffer ); + } - determinant: function( m ) { - return m[0]*m[3] - m[1]*m[2]; - }, - - inverse: function( m, result ) { - - var det = matrix2.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix2(); - - result[0] = m[3]/det; - result[1] = m[1]*-1/det; - result[2] = m[2]*-1/det; - result[3] = m[0]/det; - - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix2(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][2]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][3]; - result[2] = temp[2]*ml[i][0] + temp[3]*ml[i][2]; - result[3] = temp[2]*ml[i][1] + temp[3]*ml[i][3]; - temp = result; - } - } - return result; - }, - - transpose: function( m, result ) { - result = result || Matrix2(); - - var temp = m[1]; - result[0] = m[0]; - result[1] = m[2]; - result[2] = temp; - result[3] = m[3]; - - return result; - } + function equal( arg ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix4.equal( this.buffer, other ); + } + + function inverse( result ) { + result = result || this; + if( !matrix4.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix4.inverse( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + other = arg1.buffer; + } else { + other = arg1; } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } - Object.defineProperty( matrix2, 'zero', { - get: function() { - return Matrix2( [0, 0, - 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'one', { - get: function() { - return Matrix2( [1, 1, - 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'identity', { - get: function() { - return Matrix2( [1, 0, - 0, 1] ); - }, - enumerable: true - }); + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - return matrix2; + result = result || this; + matrix4.subtract( this.buffer, other, result.buffer ); + result.modified = true; - }; + return this; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function transpose( result ) { + result = result || this; + matrix4.transpose( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Matrix4.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); -define('matrix/matrix3',['require','./matrix'],function ( require ) { + return Matrix4; - return function( FLOAT_ARRAY_TYPE ) { + }; - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); +}); +define('matrix/transform-api',['require','common/not-implemented','matrix/m4','matrix/matrix4-api'],function ( require ) { - var Matrix3 = function() { - if( 0 === arguments.length ) { - return matrix.$( 9, [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - } else { - return matrix.$( 9, arguments ); - } - }; + return function( FLOAT_ARRAY_TYPE ) { - var matrix3 = { - - $: Matrix3, + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); - add: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + function compound( transform, t, r, s ) { - subtract: function( ml, result ) { - result = result || Matrix3(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + if( t ) { + translate( t, transform ); + } - clear: matrix.clear, + if( r ) { + rotate( r, transform ); + } - equal: matrix.equal, + if( s ) { + scale( s, transform ); + } - determinant: function( m ) { + return transform; + } - return m[0]*(m[4]*m[8] - m[5]*m[7]) - - m[1]*(m[3]*m[8] - m[5]*m[6]) - + m[2]*(m[3]*m[7] - m[4]*m[6]); - }, - - inverse: function( m, result ) { - var det = matrix3.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix3(); - - result[0] = (m[8]*m[4] - m[7]*m[5])/det; - result[1] = -(m[8]*m[1] - m[7]*[2])/det; - result[2] = (m[5]*m[1] - m[4]*m[2])/det; - - result[3] = -(m[8]*m[3] - m[6]*m[5])/det; - result[4] = (m[8]*m[0] - m[6]*m[2])/det; - result[5] = -(m[5]*m[0] - m[3]*m[2])/det; - - result[6] = (m[7]*m[3] - m[6]*m[4])/det; - result[7] = -(m[7]*m[0] - m[6]*m[1])/det; - result[8] = (m[4]*m[0] - m[3]*m[1])/det; + function set(transform, t, r, s){ + if (transform){ + matrix4.set(transform, matrix4.identity); + } + return compound(transform, t, r, s); + } - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][3] + temp[2]*ml[i][6]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][4] + temp[2]*ml[i][7]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][5] + temp[2]*ml[i][8]; - - result[3] = temp[3]*ml[i][0] + temp[4]*ml[i][3] + temp[5]*ml[i][6]; - result[4] = temp[3]*ml[i][1] + temp[4]*ml[i][4] + temp[5]*ml[i][7]; - result[5] = temp[3]*ml[i][2] + temp[4]*ml[i][5] + temp[5]*ml[i][8]; - - result[6] = temp[6]*ml[i][0] + temp[7]*ml[i][3] + temp[8]*ml[i][6]; - result[7] = temp[6]*ml[i][1] + temp[7]*ml[i][4] + temp[8]*ml[i][7]; - result[8] = temp[6]*ml[i][2] + temp[7]*ml[i][5] + temp[8]*ml[i][8]; - - temp = result; - } - } - return result; - }, - - // Convert a vector rotation (in radians) to a 3x3 matrix - rotate: function( v, result ) { - var r = result || matrix4.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix3.$([ cosA, sinA, 0, - -sinA, cosA, 0, - 0, 0, 1 ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + function rotate( v, result ) { + result = result || new M4( matrix4.identity ); - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix3.$([ cosA, 0, -sinA, - 0, 1, 0, - sinA, 0, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + var sinA, + cosA; + var rotation; - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix3.$([ 1, 0, 0, - 0, cosA, sinA, - 0, -sinA, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + if( 0 !== v[2] ) { + sinA = Math.sin( v[2] ); + cosA = Math.cos( v[2] ); - if( !result ) { - return r; - } - }, + rotation = [ cosA, -sinA, 0, 0, + sinA, cosA, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - transpose: function( m, result ) { - result = result || Matrix3(); + if( 0 !== v[1] ) { + sinA = Math.sin( v[1] ); + cosA = Math.cos( v[1] ); - var a01 = m[1], a02 = m[2], a12 = m[5]; - - result[0] = m[0]; - result[1] = m[3]; - result[2] = m[6]; - result[3] = a01; - result[4] = m[4]; - result[5] = m[7]; - result[6] = a02; - result[7] = a12; - result[8] = m[8]; - - return result; - } + rotation = [ cosA, 0, sinA, 0, + 0, 1, 0, 0, + -sinA, 0, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - }; - - Object.defineProperty( matrix3, 'zero', { - get: function() { - return Matrix3( [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix3, 'one', { - get: function() { - return Matrix3( [1, 1, 1, - 1, 1, 1, - 1, 1, 1] ); - }, - enumerable: true - }); + if( 0 !== v[0] ) { + sinA = Math.sin( v[0] ); + cosA = Math.cos( v[0] ); - Object.defineProperty( matrix3, 'identity', { - get: function() { - return Matrix3( [1, 0, 0, - 0, 1, 0, - 0, 0, 1] ); - }, - enumerable: true - }); + rotation = [ 1, 0, 0, 0, + 0, cosA, -sinA, 0, + 0, sinA, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } + + return result; + } + + function scale( v, result ) { + result = result || new M4( matrix4.identity ); + + matrix4.multiply( result, [v[0], 0, 0, 0, + 0, v[1], 0, 0, + 0, 0, v[2], 0, + 0, 0, 0, 1], result ); + + return result; + } + + function translate( v, result ) { + result = result || new M4( matrix4.identity ); - return matrix3; + matrix4.multiply( result, [1, 0, 0, v[0], + 0, 1, 0, v[1], + 0, 0, 1, v[2], + 0, 0, 0, 1], result ); + + return result; + } + var transform = { + compound: compound, + set: set, + rotate: rotate, + scale: scale, + translate: translate }; + return transform; + + }; + }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/t',['require','matrix/m','matrix/m4','matrix/transform-api'],function ( require ) { -define('matrix/matrix4',['require','./matrix','../vector/vector3'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { - return function( FLOAT_ARRAY_TYPE ) { + var M = require( "matrix/m" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require("matrix/transform-api")( FLOAT_ARRAY_TYPE ); - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( '../vector/vector3' )( FLOAT_ARRAY_TYPE ); + var T = function(t, r, s) { + var matrix = new M4(); + return transform.set(matrix, t, r, s); + }; + T.prototype = new M(); + T.prototype.constructor = T; - var Matrix4 = function() { - if( 0 === arguments.length ) { - return matrix.$( 16, [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - } else { - return matrix.$( 16, arguments ); - } - }; + return T; - var matrix4 = { - - $: Matrix4, + }; - add: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, - - subtract: function( ml, result ) { - result = result || Matrix4(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, +}); +define('matrix/transform',['require','common/not-implemented','matrix/m4','matrix/transform-api','matrix/matrix4-api','matrix/matrix4'],function ( require ) { - clear: matrix.clear, + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require( "matrix/transform-api" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix4 = require( "matrix/matrix4" )( FLOAT_ARRAY_TYPE ); + + function getView( index ) { + return this._views[index]; + } - equal: matrix.equal, + function getValue( index ) { + return this.buffer[index]; + } - multiply: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][4] + temp[2]*ml[i][8] + temp[3]*ml[i][12]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][5] + temp[2]*ml[i][9] + temp[3]*ml[i][13]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][6] + temp[2]*ml[i][10] + temp[3]*ml[i][14]; - result[3] = temp[0]*ml[i][3] + temp[1]*ml[i][7] + temp[2]*ml[i][11] + temp[3]*ml[i][15]; - result[4] = temp[4]*ml[i][0] + temp[5]*ml[i][4] + temp[6]*ml[i][8] + temp[7]*ml[i][12]; - result[5] = temp[4]*ml[i][1] + temp[5]*ml[i][5] + temp[6]*ml[i][9] + temp[7]*ml[i][13]; - result[6] = temp[4]*ml[i][2] + temp[5]*ml[i][6] + temp[6]*ml[i][10] + temp[7]*ml[i][14]; - result[7] = temp[4]*ml[i][3] + temp[5]*ml[i][7] + temp[6]*ml[i][11] + temp[7]*ml[i][15]; - result[8] = temp[8]*ml[i][0] + temp[9]*ml[i][4] + temp[10]*ml[i][8] + temp[11]*ml[i][12]; - result[9] = temp[8]*ml[i][1] + temp[9]*ml[i][5] + temp[10]*ml[i][9] + temp[11]*ml[i][13]; - result[10] = temp[8]*ml[i][2] + temp[9]*ml[i][6] + temp[10]*ml[i][10] + temp[11]*ml[i][14]; - result[11] = temp[8]*ml[i][3] + temp[9]*ml[i][7] + temp[10]*ml[i][11] + temp[11]*ml[i][15]; - result[12] = temp[12]*ml[i][0] + temp[13]*ml[i][4] + temp[14]*ml[i][8] + temp[15]*ml[i][12]; - result[13] = temp[12]*ml[i][1] + temp[13]*ml[i][5] + temp[14]*ml[i][9] + temp[15]*ml[i][13]; - result[14] = temp[12]*ml[i][2] + temp[13]*ml[i][6] + temp[14]*ml[i][10] + temp[15]*ml[i][14]; - result[15] = temp[12]*ml[i][3] + temp[13]*ml[i][7] + temp[14]*ml[i][11] + temp[15]*ml[i][15]; - - temp = result; - } - } - return result; - }, - - multiplyVector3: function( m, v, result ) { - result = result || vector3.$(); - - result[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12]; - result[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13]; - result[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14]; + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - return result; - }, + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new TransformView( this, this.buffer, + i*4, (i+1)*4 ); + } + } - determinant: function (m) { - var a0 = m[0] * m[5] - m[1] * m[4]; - var a1 = m[0] * m[6] - m[2] * m[4]; - var a2 = m[0] * m[7] - m[3] * m[4]; - var a3 = m[1] * m[6] - m[2] * m[5]; - var a4 = m[1] * m[7] - m[3] * m[5]; - var a5 = m[2] * m[7] - m[3] * m[6]; - var b0 = m[8] * m[13] - m[9] * m[12]; - var b1 = m[8] * m[14] - m[10] * m[12]; - var b2 = m[8] * m[15] - m[11] * m[12]; - var b3 = m[9] * m[14] - m[10] * m[13]; - var b4 = m[9] * m[15] - m[11] * m[13]; - var b5 = m[10] * m[15] - m[11] * m[14]; - - var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; - - return det; - }, + var TransformView = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - transpose: function (m , result) { - result = result || Matrix4(); - - result[0] = m[0]; - result[1] = m[4]; - result[2] = m[8]; - result[3] = m[12]; - result[4] = m[1]; - result[5] = m[5]; - result[6] = m[9]; - result[7] = m[13]; - result[8] = m[2]; - result[9] = m[6]; - result[10] = m[10]; - result[11] = m[14]; - result[12] = m[3]; - result[13] = m[7]; - result[14] = m[11]; - result[15] = m[15]; - - return result; - }, + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + }; - inverse: function (m, result) { - - result = result || Matrix4(); - - var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], - a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], - a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], - a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), - invDet; - - // Determinant, throw exception if singular - if (!d) - throw 'matrix is singular'; - - invDet = 1 / d; - result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; - result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; - result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; - result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; - result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; - result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; - result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; - result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; - result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; - result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; - result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; - result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; - result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; - result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; - result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; - result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; - - return result; - }, + var Transform = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Transform || + arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else if( arg1 instanceof M4 ) { + this.buffer = new M4( arg1 ); + } else { + this.buffer = transform.compound( arg1, arg2, arg3 ); + } + } else { + this.buffer = transform.compound( arg1, arg2, arg3 ); + } - toHTML: function( m ) { - var result = "[ "; - for( var i = 0; i < 4; ++ i ) { - result += "
    "; - for( var j = 0; j < 4; ++ j ) { - result += " (" + m[4*i+j] + ") "; - } - } - result += " ]"; - return result; - } + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); - }; - Object.defineProperty( matrix4, 'zero', { - get: function() { - return Matrix4( [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'one', { - get: function() { - return Matrix4( [1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'identity', { - get: function() { - return Matrix4( [1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1] ); - }, - enumerable: true - }); + this._views = []; - return matrix4; + updateViews.call( this ); + this.modified = true; }; -}); + function clone() { + return new Transform( this ); + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, -nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function equal( arg ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } -define('matrix/transform',['require','./matrix4'],function ( require ) { + return matrix4.equal( this.buffer, other ); + } - return function( FLOAT_ARRAY_TYPE ) { + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } - var matrix4 = require( './matrix4' )( FLOAT_ARRAY_TYPE ); + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; - var Transform = matrix4.$; - - var transform = { - - $: Transform, + return this; + } - fixed: function( vt, vr, vs ) { - var r = matrix4.identity; + function rotate( v, result ) { + var rotation = transform.rotate( v ); - if( vt ) { - transform.translate( vt, r ); - } + result = result || this; + matrix4.multiply( this.buffer, rotation, result.buffer ); + result.modified = true; - if( vr ) { - transform.rotate( vr, r ); - } + return this; + } - if( vs ) { - transform.scale( vs, r ); - } + function scale( v, result ) { + var scaled = transform.scale( v ); - return r; - }, + result = result || this; + matrix4.multiply( this.buffer, scaled, result.buffer ); + result.modified = true; - // Convert a vector rotation (in radians) to a 4x4 matrix - rotate: function( v, result ) { - var r = result || matrix4.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix4.$([ cosA, sinA, 0, 0, - -sinA, cosA, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + return this; + } - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix4.$([ cosA, 0, -sinA, 0, - 0, 1, 0, 0, - sinA, 0, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function set( t, r, s ) { + transform.compound( this.buffer, t, r, s ); + this.modified = true; + } - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix4.$([ 1, 0, 0, 0, - 0, cosA, sinA, 0, - 0, -sinA, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function transformDirection( v, result ) { - if( !result ) { - return r; - } - }, + } - // Convert a vector3 scale to a 4x4 matrix - scale: function( v, result ) { - var r = [ v[0], 0.0, 0.0, 0.0, - 0.0, v[1], 0.0, 0.0, - 0.0, 0.0, v[2], 0.0, - 0.0, 0.0, 0.0, 1.0 ]; + function transformPoint( v, result ) { - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - }, + } - // Convert a vector3 translation to a 4x4 matrix - translate: function( v, result ) { - var r = [ 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - v[0], v[1], v[2], 1.0 ] + function translate( v, result ) { + var translation = transform.translate( v ); - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - } + result = result || this; + matrix4.multiply( this.buffer, translation, result.buffer ); + result.modified = true; - }; - - return transform; + return this; + } + Transform.prototype = { + clone: clone, + equal: equal, + inverseTransformDirection: notImplemented, + inverseTransformPoint: notImplemented, + multiply: multiply, + rotate: rotate, + scale: scale, + set: set, + transformDirection: notImplemented, + transformPoint: notImplemented, + translate: translate }; + return Transform; + + }; + }); +define('_math',['require','constants','equal','vector/v2','vector/vector2','vector/vector2-api','vector/v3','vector/vector3','vector/vector3-api','vector/v4','vector/vector4','vector/vector4-api','matrix/m2','matrix/matrix2','matrix/matrix2-api','matrix/m3','matrix/matrix3','matrix/matrix3-api','matrix/m4','matrix/matrix4','matrix/matrix4-api','matrix/t','matrix/transform','matrix/transform-api'],function ( require ) { -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var constants = require( "constants" ); + var equal = require( "equal" ); -define('_math',['require','./lang','./constants','./vector/vector2','./vector/vector3','./vector/vector4','./vector/quaternion','./matrix/matrix2','./matrix/matrix3','./matrix/matrix4','./matrix/transform'],function ( require ) { + var V2 = require( "vector/v2" ); + var Vector2 = require( "vector/vector2" ); + var vector2 = require( "vector/vector2-api" ); - var lang = require( './lang' ), - constants = require( './constants' ), - vector2 = require( './vector/vector2' ), - vector3 = require( './vector/vector3' ), - vector4 = require( './vector/vector4' ), - quaternion = require( './vector/quaternion' ), - matrix2 = require( './matrix/matrix2' ), - matrix3 = require( './matrix/matrix3' ), - matrix4 = require( './matrix/matrix4' ), - transform = require( './matrix/transform' ); + var V3 = require( "vector/v3" ); + var Vector3 = require( "vector/vector3" ); + var vector3 = require( "vector/vector3-api" ); - var _Math = function( options ) { - - var _FLOAT_ARRAY_ENUM = { - Float32: Float32Array, - Float64: Float64Array - }; - - var _FLOAT_ARRAY_TYPE = _FLOAT_ARRAY_ENUM.Float32; - - Object.defineProperty( this, 'ARRAY_TYPE', { - get: function() { - return _FLOAT_ARRAY_TYPE; - }, - enumerable: true - }); - - lang.extend( this, constants() ); - - var _vector2 = vector2( _FLOAT_ARRAY_TYPE ); - var _vector3 = vector3( _FLOAT_ARRAY_TYPE ); - var _vector4 = vector4( _FLOAT_ARRAY_TYPE ); - var _quaternion = quaternion( _FLOAT_ARRAY_TYPE ); - - var _matrix2 = matrix2( _FLOAT_ARRAY_TYPE ); - var _matrix3 = matrix3( _FLOAT_ARRAY_TYPE ); - var _matrix4 = matrix4( _FLOAT_ARRAY_TYPE ); - var _transform = transform( _FLOAT_ARRAY_TYPE ); - - Object.defineProperty( this, 'Vector2', { - get: function() { - return _vector2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector2', { - get: function() { - return _vector2; - }, - enumerable: true - }); + var V4 = require( "vector/v4" ); + var Vector4 = require( "vector/vector4" ); + var vector4 = require( "vector/vector4-api" ); - Object.defineProperty( this, 'Vector3', { - get: function() { - return _vector3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector3', { - get: function() { - return _vector3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Vector4', { - get: function() { - return _vector4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector4', { - get: function() { - return _vector4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Quaternion', { - get: function() { - return _quaternion.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'quaternion', { - get: function() { - return _quaternion; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix2', { - get: function() { - return _matrix2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix2', { - get: function() { - return _matrix2; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix3', { - get: function() { - return _matrix3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix3', { - get: function() { - return _matrix3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix4', { - get: function() { - return _matrix4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix4', { - get: function() { - return _matrix4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Transform', { - get: function() { - return _transform.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'transform', { - get: function() { - return _transform; - }, - enumerable: true - }); - + var M2 = require( "matrix/m2" ); + var Matrix2 = require( "matrix/matrix2" ); + var matrix2 = require( "matrix/matrix2-api" ); + + var M3 = require( "matrix/m3" ); + var Matrix3 = require( "matrix/matrix3" ); + var matrix3 = require( "matrix/matrix3-api" ); + + var M4 = require( "matrix/m4" ); + var Matrix4 = require( "matrix/matrix4" ); + var matrix4 = require( "matrix/matrix4-api" ); + + var T = require( "matrix/t" ); + var Transform = require( "matrix/transform" ); + var transform = require( "matrix/transform-api" ); + + function extend( object, extra ) { + for ( var prop in extra ) { + if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { + object[prop] = extra[prop]; + } + } + } + + var _Math = function( options ) { + var FLOAT_ARRAY_ENUM = { + Float32: Float32Array, + Float64: Float64Array }; + this.FLOAT_ARRAY_ENUM = FLOAT_ARRAY_ENUM; + + var ARRAY_TYPE = this.ARRAY_TYPE = FLOAT_ARRAY_ENUM.Float32; + + extend( this, constants ); + this.equal = equal; + extend( this, { + V2: V2( ARRAY_TYPE ), + Vector2: Vector2( ARRAY_TYPE ), + vector2: vector2( ARRAY_TYPE ) + }); + extend( this, { + V3: V3( ARRAY_TYPE ), + Vector3: Vector3( ARRAY_TYPE ), + vector3: vector3( ARRAY_TYPE ) + }); + extend( this, { + V4: V4( ARRAY_TYPE ), + Vector4: Vector4( ARRAY_TYPE ), + vector4: vector4( ARRAY_TYPE ) + }); + extend( this, { + M2: M2( ARRAY_TYPE ), + Matrix2: Matrix2( ARRAY_TYPE ), + matrix2: matrix2( ARRAY_TYPE ) + }); + extend( this, { + M3: M3( ARRAY_TYPE ), + Matrix3: Matrix3( ARRAY_TYPE ), + matrix3: matrix3( ARRAY_TYPE ) + }); + extend( this, { + M4: M4( ARRAY_TYPE ), + Matrix4: Matrix4( ARRAY_TYPE ), + matrix4: matrix4( ARRAY_TYPE ) + }); + extend( this, { + T: T( ARRAY_TYPE ), + Transform: Transform( ARRAY_TYPE ), + transform: transform( ARRAY_TYPE ) + }); + }; - return new _Math(); + return new _Math(); }); - return require( "_math" ); + return require('_math'); })); // Note: Some Emscripten settings will significantly limit the speed of the generated code. @@ -90405,9 +99613,9 @@ define('src/services/resolver',['require','base/service','core/event','_math','b }; Service.call( this, scheduler, schedules ); - options.gravity = options.gravity || [0, 0]; this.gravity = new Box2D.b2Vec2(); this.world = new Box2D.b2World( this.gravity ); + this.dimensionMap = options.dimensionMap || 0; this._timeStep = 30; // time step, in milliseconds this._timeRemaining = 0; // time remaining from last frame, in milliseconds @@ -90462,6 +99670,14 @@ define('src/services/resolver',['require','base/service','core/event','_math','b this.world.SetContactListener( contactListener ); }; + var totalForce = new math.Vector2(); + + var DimensionMaps = { + XY: 0, + XZ: 1, + YZ: 2 + }; + function resolve() { var component; @@ -90476,13 +99692,13 @@ define('src/services/resolver',['require','base/service','core/event','_math','b // add up all the global forces into gravity, // and then set the gravity on the world // TODO: Make sure that we transform each force according to the transforms of whatever parent objects it has - var totalForce = new math.Vector2(); + totalForce.clear(); var entityId; for (entityId in registeredComponents["Force"]){ - math.vector2.add(totalForce, registeredComponents["Force"][entityId].force, totalForce); + totalForce.add(registeredComponents["Force"][entityId].force); } - this.gravity.Set(totalForce[0], totalForce[1]); + this.gravity.Set(totalForce.x, totalForce.y); this.world.SetGravity(this.gravity); // Update all physics components @@ -90507,6 +99723,7 @@ define('src/services/resolver',['require','base/service','core/event','_math','b Resolver.prototype = new Service(); Resolver.prototype.constructor = Resolver; Resolver.prototype.resolve = resolve; + Resolver.prototype.DimensionMaps = DimensionMaps; return Resolver; @@ -90620,6 +99837,8 @@ define('src/components/body',['require','box2d','common/extend','base/component' var that = this; var i; + this.service = service; + if( options.bodyDefinition) { this.box2dBody = service.world.CreateBody( options.bodyDefinition ); } else { @@ -90645,12 +99864,28 @@ define('src/components/body',['require','box2d','common/extend','base/component' Body.prototype = new Component(); Body.prototype.constructor = Body; - var linearImpulse = new Box2D.b2Vec2( 0, 0 ); + var linearVector = new Box2D.b2Vec2( 0, 0 ); + + function setAngularVelocity(rotation){ + this.box2dBody.SetAngularVelocity(rotation); + } + + function setLinearVelocity(arg1, arg2) { + var argc = arguments.length; + if( 1 === argc ) { + linearVector.Set( arg1[0], arg1[1] ); + }else{ + linearVector.Set( arg1, arg2); + } + this.box2dBody.SetLinearVelocity( linearVector ); + linearVector.Set( 0, 0 ); + } + function onLinearImpulse( event ) { var impulse = event.data.impulse; - linearImpulse.Set( impulse[0], impulse[1] ); - this.box2dBody.ApplyLinearImpulse( linearImpulse, this.box2dBody.GetPosition() ); - linearImpulse.Set( 0, 0 ); + linearVector.Set( impulse[0], impulse[1] ); + this.box2dBody.ApplyLinearImpulse( linearVector, this.box2dBody.GetPosition() ); + linearVector.Set( 0, 0 ); } function onAngularImpulse( event ) { @@ -90661,10 +99896,19 @@ define('src/components/body',['require','box2d','common/extend','base/component' var position2 = this.box2dBody.GetPosition(); var angle2 = this.box2dBody.GetAngle(); - // TD: This will cause the transform to emit an event that we handle below. Blech! + var transform = this.owner.findComponent( "Transform" ); - transform.setPosition( math.Vector3( position2.get_x(), position2.get_y(), transform.position[2] ) ); - transform.setRotation( math.Vector3( transform.rotation[0], transform.rotation[1], angle2 ) ); + //Note: It is currently okay to read from buffers, but writing to them will result in things breaking + if (this.service.dimensionMap === this.service.DimensionMaps.XY){ + transform.position = [ position2.get_x(), position2.get_y(), transform.position.buffer[2] ]; + transform.rotation.z = angle2; + }else if (this.service.dimensionMap === this.service.DimensionMaps.XZ){ + transform.position = [ position2.get_x(), transform.position.buffer[1], position2.get_y()]; + transform.rotation.y = angle2; + }else{ + transform.position = [transform.position.buffer[0], position2.get_y(), position2.get_x()]; + transform.rotation.x = angle2; + } } function onEntitySpaceChanged( event ) { @@ -90692,7 +99936,14 @@ define('src/components/body',['require','box2d','common/extend','base/component' if( this.owner ) { var transform = this.owner.findComponent( 'Transform' ); - this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position[0], transform.position[1] ), transform.rotation[2] ); + //Note: It is currently okay to read from buffers, but writing to them will result in things breaking + if (this.service.dimensionMap === this.service.DimensionMaps.XY){ + this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[0], transform.position.buffer[1] ), transform.rotation.buffer[2] ); + }else if (this.service.dimensionMap === this.service.DimensionMaps.XZ){ + this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[0], transform.position.buffer[2] ), transform.rotation.buffer[1] ); + }else{ + this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[2], transform.position.buffer[1] ), transform.rotation.buffer[0] ); + } } if( this.owner === null && data.previous !== null ) { @@ -90716,6 +99967,8 @@ define('src/components/body',['require','box2d','common/extend','base/component' } var prototype = { + setAngularVelocity: setAngularVelocity, + setLinearVelocity: setLinearVelocity, onLinearImpulse: onLinearImpulse, onAngularImpulse: onAngularImpulse, onUpdate: onUpdate, @@ -90809,6 +100062,8 @@ define('src/resources/body-definition',['require','box2d'],function ( require ) var box2dBodyDef = new Box2D.b2BodyDef(); box2dBodyDef._gladius = {}; + box2dBodyDef.set_bullet(options.hasOwnProperty( 'bullet' ) ? + options.bullet : false); box2dBodyDef.set_type( options.hasOwnProperty( 'type' ) ? options.type : Box2D.b2_dynamicBody ); box2dBodyDef.set_linearDamping( options.hasOwnProperty( 'linearDamping' ) ? @@ -90846,6 +100101,20 @@ define('src/resources/fixture-definition',['require','box2d'],function ( require var box2dFixtureDef = new Box2D.b2FixtureDef(); box2dFixtureDef._gladius = {}; box2dFixtureDef.set_density( options.hasOwnProperty( 'density' ) ? options.density : 1 ); + box2dFixtureDef.set_friction( options.hasOwnProperty( 'friction' ) ? options.friction : 0.2); + box2dFixtureDef.set_restitution( options.hasOwnProperty( 'restitution' ) ? options.restitution : 0); + if (options.hasOwnProperty( 'filter' )){ + var filter = box2dFixtureDef.get_filter(); + if (options.filter.hasOwnProperty( 'groupIndex' )){ + filter.set_groupIndex(options.filter.groupIndex); + } + if (options.filter.hasOwnProperty( 'categoryBits' )){ + filter.set_categoryBits(options.filter.categoryBits); + } + if (options.filter.hasOwnProperty( 'maskBits' )){ + filter.set_maskBits(options.filter.maskBits); + } + } box2dFixtureDef.set_shape( options.shape ); return box2dFixtureDef; }; @@ -90861,8 +100130,8 @@ if ( typeof define !== "function" ) { define('src/resources/box-shape',['require','box2d'],function ( require ) { require( "box2d" ); var BoxShape = function( hx, hy ) { - hx = hx || 1; - hy = hy || 1; + hx = hx/2 || 0.5; + hy = hy/2 || 0.5; var box2dPolygonShape = new Box2D.b2PolygonShape(); box2dPolygonShape._gladius = {}; box2dPolygonShape.SetAsBox( hx, hy ); @@ -90870,11 +100139,26 @@ define('src/resources/box-shape',['require','box2d'],function ( require ) { }; return BoxShape; }); + +if ( typeof define !== "function" ) { + var define = require( "amdefine" )( module ); +} + +define('src/resources/circle-shape',['require','box2d'],function ( require ) { + require( "box2d" ); + var CircleShape = function( radius ) { + var box2dCircleShape = new Box2D.b2CircleShape(); + box2dCircleShape._gladius = {}; + box2dCircleShape.set_m_radius(radius); + return box2dCircleShape; + }; + return CircleShape; +}); if ( typeof define !== "function" ) { var define = require( "amdefine" )( module ); } -define('../src/gladius-box2d',['require','base/extension','src/services/resolver','src/components/body','src/components/force','src/resources/body-definition','src/resources/fixture-definition','src/resources/box-shape'],function ( require ) { +define('../src/gladius-box2d',['require','base/extension','src/services/resolver','src/components/body','src/components/force','src/resources/body-definition','src/resources/fixture-definition','src/resources/box-shape','src/resources/circle-shape'],function ( require ) { var Extension = require( "base/extension" ); @@ -90898,7 +100182,8 @@ define('../src/gladius-box2d',['require','base/extension','src/services/resolver resources: { "BodyDefinition": require( "src/resources/body-definition" ), "FixtureDefinition": require( "src/resources/fixture-definition" ), - "BoxShape": require( "src/resources/box-shape" ) + "BoxShape": require( "src/resources/box-shape" ), + "CircleShape": require( "src/resources/circle-shape" ) } }); diff --git a/gladius/gladius-core.js b/gladius/gladius-core.js index 69782b8..b6c09ad 100644 --- a/gladius/gladius-core.js +++ b/gladius/gladius-core.js @@ -618,1683 +618,11045 @@ var requirejs, require, define; define("../tools/almond", function(){}); -/*jshint white: false, strict: false, plusplus: false */ -/*global define: false */ - -//JS language helpers. - -//Array Remove - By John Resig (MIT Licensed) -//Done outside the define call since it should be immediately -//before dependency tracing is done for any module. -if ( !Array.prototype.remove ) { - Array.prototype.remove = function(from, to) { - var rest = this.slice( (to || from) + 1 || this.length ); - this.length = from < 0 ? this.length + from : from; - return this.push.apply(this, rest); +define('constants',['require'],function ( require ) { + + return { + TAU: 2 * Math.PI, + PI: Math.PI + }; + +}); +define('equal',['require'],function ( require ) { + + function equal( arg1, arg2, e ) { + e = e || 0.000001; + + return Math.abs( arg1 - arg2 ) < e; + } + + return equal; + +}); +define('vector/v',['require'],function ( require ) { + + var V = function() { + }; + + return V; + +}); +define('vector/v2',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V2 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 2 ); + + for( i = 0; i < argc && vi < 2; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 2; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 2; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V2.prototype = new V(); + V2.prototype.constructor = V2; + + return V2; + + }; + +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + + + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } + + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } + + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, + + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, + + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, + + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; + + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; + + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; + + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; + + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; + + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; + + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; + + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } + + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } + + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); + + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } + + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } + + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); + + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); + + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); + + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); + + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); + + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); + + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } + + var length = collection.length, + noaccum = arguments.length < 3; + + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; + + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } + + var prop, + props = keys(collection); + + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } + + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); + + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); + + + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); + + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); + + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } + + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } + + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } + + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } + + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } + + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } + + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } + + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } + + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } + + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); + + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } + + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } + + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } + + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; + } + + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } + + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } + + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } + + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } + + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } + + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } + + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; + + return function() { + var result, + others = arguments; + + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } + + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } + + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { + return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } + + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; + + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } + + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } + + var index = -1, + result = true, + size = 0; + + // add the first collection to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } + + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } + + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } + + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; + + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } + + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } + + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } + + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } + + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } + + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; + + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; + + // clear stored code snippets + tokenized.length = 0; + + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; + + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } + + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } + + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } + + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } + + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } + + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } + + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } + + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; + }; + }); + + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); + + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('vector/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); + +define('common/not-implemented',['require'],function ( require ) { + + return function notImplemented() { + throw new Error( "not implemented" ); + }; + +}); +define('vector/vector',['require'],function ( require ) { + + var Vector = function() { + }; + + return Vector; + +}); +define('vector/vector2-api',['require','common/not-implemented','vector/v2'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + + return result; + } + + function angle( v1, v2 ) { + var normalizedV1 = new V2(); + var normalizedV2 = new V2(); + + normalize(v1, normalizedV1); + normalize(v2, normalizedV2); + + return Math.acos( dot( normalizedV1, normalizedV2 ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + + return v; + } + + function distance( v1, v2 ) { + return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1])); + } + + function dot( v1, v2 ) { + var r = 0; + + r += v1[0] * v2[0]; + r += v1[1] * v2[1]; + + return r; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + + return Math.sqrt( r ); + } + + function limit(v, max, result){ + result = result || new V2(); + var length; + length = Math.sqrt( v[0] * v[0] + + v[1] * v[1]); + + if (length > max){ + var ratio = max/length; + result[0] = v[0] * ratio; + result[1] = v[1] * ratio; + }else{ + result[0] = v[0]; + result[1] = v[1]; + } + return result; + } + + function multiply( v, s, result ) { + result = result || new V2(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + + return result; + } + + function negate( v, result ) { + result = result || new V2(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + + return result; + } + + function normalize( v, result ) { + result = result || new V2(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + + return result; + } + + function project( v1, v2, result ) { + result = result || new V2(); + + var dp = v1[0]*v2[0] + v1[1]*v2[1]; + var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); + + result[0] = dp_over_v2_squared_length * v2[0]; + result[1] = dp_over_v2_squared_length * v2[1]; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + + return result; + } + + var vector2 = { + add: add, + angle: angle, + clear: clear, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract, + + x: new V2( 1, 0 ), + u: new V2( 1, 0 ), + y: new V2( 0, 1 ), + v: new V2( 0, 1 ), + zero: new V2( 0, 0 ), + one: new V2( 1, 1 ) + }; + + return vector2; + + }; + +}); +define('vector/vector2',['require','../../lib/lodash','common/not-implemented','vector/v2','vector/vector2-api','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); + var vector2 = require( "vector/vector2-api" )( FLOAT_ARRAY_TYPE ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector2 = function( arg1, arg2 ) { + var argc = arguments.length; + + this.buffer = new V2( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); + + this.modified = true; + this.size = 2; + }; + Vector2.prototype = new Vector(); + Vector2.prototype.constructor = Vector2; + + function add( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.angle( this.buffer, other ); + } + + function clear() { + vector2.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector2( this ); + } + + function distance(arg) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + return vector2.distance(this.buffer, other); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector2.equal( this.buffer, other ); + } + + function length() { + return vector2.length( this.buffer ); + } + + function limit(max, result) { + result = result || this; + var other; + if( result instanceof Vector2 ) { + other = result.buffer; + result.modified = true; + } else { + other = result; + } + vector2.limit(this.buffer, max, other); + return result; + } + + function multiply( arg, result ) { + result = result || this; + vector2.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector2.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector2.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function project( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.project( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector2 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + this.modified = true; + } + } else if( 2 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector2.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector2.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract + }); + + return Vector2; + + }; + +}); +define('vector/v3',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V3 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 3 ); + + for( i = 0; i < argc && vi < 3; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 3; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 3; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V3.prototype = new V(); + V3.prototype.constructor = V3; + + return V3; + + }; + +}); +define('matrix/matrix',['require'],function ( require ) { + + var Matrix = function() { + }; + + return Matrix; + +}); +define('vector/vector3-api',['require','common/not-implemented','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + return; + } + + if( undefined === result ) { + result = new V3( v1[0] + v2[0], + v1[1] + v2[1], v1[2] + v2[2] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + + return v; + } + + function cross( v1, v2, result ) { + result = result || new V3(); + + var v1_0 = v1[0], + v1_1 = v1[1], + v1_2 = v1[2]; + var v2_0 = v2[0], + v2_1 = v2[1], + v2_2 = v2[2]; + + result[0] = (v1_1 * v2_2) - (v2_1 * v1_2); + result[1] = (v1_2 * v2_0) - (v2_2 * v1_0); + result[2] = (v1_0 * v2_1) - (v2_0 * v1_1); + + return result; + } + + function distance( v1, v2 ) { + return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]) + + (v1[2] - v2[2]) * (v1[2] - v2[2])); + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + + return Math.sqrt( r ); + } + + function limit(v, max, result){ + result = result || new V3(); + var length; + length = Math.sqrt( v[0] * v[0] + + v[1] * v[1] + + v[2] * v[2]); + + if (length > max){ + var ratio = max/length; + result[0] = v[0] * ratio; + result[1] = v[1] * ratio; + result[2] = v[2] * ratio; + }else{ + result[0] = v[0]; + result[1] = v[1]; + result[2] = v[2]; + } + return result; + } + + function multiply( v, s, result ) { + result = result || new V3(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + + return result; + } + + function negate( v, result ) { + result = result || new V3(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + + return result; + } + + function normalize( v, result ) { + result = result || new V3(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V3(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + + return result; + } + + //This does a matrix3 by vector3 transform, which is a matrix multiplication + //The matrix3 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V3(); + + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[1] * y + m[2] * z; + result[1] = m[3] * x + m[4] * y + m[5] * z; + result[2] = m[6] * x + m[7] * y + m[8] * z; + + return result; + } + + var vector3 = { + add: add, + angle: angle, + clear: clear, + cross: cross, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V3( 1, 0, 0 ), + y: new V3( 0, 1, 0 ), + z: new V3( 0, 0, 1 ), + zero: new V3( 0, 0, 0 ), + one: new V3( 1, 1, 1 ) + }; + + return vector3; + + }; + +}); +define('vector/vector3',['require','../../lib/lodash','common/not-implemented','vector/v3','vector/vector3-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + var vector3 = require( "vector/vector3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector3 = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + + this.buffer = new V3( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + + this.modified = true; + this.size = 3; + }; + Vector3.prototype = new Vector(); + Vector3.prototype.constructor = Vector3; + + function add( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.angle( this.buffer, other ); + } + + function clear() { + vector3.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector3( this ); + } + + function cross( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.cross( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function distance(arg) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + return vector3.distance(this.buffer, other); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.equal( this.buffer, other ); + } + + function length() { + return vector3.length( this.buffer ); + } + + function limit(max, result) { + result = result || this; + var other; + if( result instanceof Vector3 ) { + other = result.buffer; + result.modified = true; + } else { + other = result; + } + vector3.limit(this.buffer, max, other); + return result; + } + + function multiply( arg, result ) { + result = result || this; + vector3.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector3.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector3.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector3 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + this.modified = true; + } + } else if( 3 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector3.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + cross: cross, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector3; + + }; + +}); +define('vector/v4',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V4 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 4 ); + + for( i = 0; i < argc && vi < 4; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 4; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 4; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V4.prototype = new V(); + V4.prototype.constructor = V4; + + return V4; + + }; + +}); +define('vector/vector4-api',['require','common/not-implemented','vector/v4'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + v1[3] += v2[3]; + return; + } + + if( undefined === result ) { + result = new V4( v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + result[3] = v1[3] + v2[3]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + v[3] = 0; + + return v; + } + + function distance( v1, v2 ) { + return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]) + + (v1[2] - v2[2]) * (v1[2] - v2[2]) + + (v1[3] - v2[3]) * (v1[3] - v2[3])); + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + var d3 = Math.abs( v1[3] - v2[3] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + r += v[3] * v[3]; + + return Math.sqrt( r ); + } + + function limit(v, max, result){ + result = result || new V4(); + var length; + length = Math.sqrt( v[0] * v[0] + + v[1] * v[1] + + v[2] * v[2] + + v[3] * v[3]); + if (length > max){ + var ratio = max/length; + result[0] = v[0] * ratio; + result[1] = v[1] * ratio; + result[2] = v[2] * ratio; + result[3] = v[3] * ratio; + }else{ + result[0] = v[0]; + result[1] = v[1]; + result[2] = v[2]; + result[3] = v[3]; + } + return result; + } + + function multiply( v, s, result ) { + result = result || new V4(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + result[3] = s * v[3]; + + return result; + } + + function negate( v, result ) { + result = result || new V4(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + result[3] = -1 * v[3]; + + return result; + } + + function normalize( v, result ) { + result = result || new V4(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + result[3] = v[3]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + v[3] = arguments[1][3]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + v[3] = arguments[4]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V4(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + result[3] = v1[3] - v2[3]; + + return result; + } + + //This does a matrix4 by vector4 transform, which is a matrix multiplication + //The matrix4 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V4(); + + var x = v[0], y = v[1], z = v[2], w = v[3]; + + result[0] = m[0] * x + m[1] * y + m[2] * z + m[3] * w; + result[1] = m[4] * x + m[5] * y + m[6] * z + m[7] * w; + result[2] = m[8] * x + m[9] * y + m[10] * z + m[11] * w; + result[3] = m[12] * x + m[13] * y + m[14] * z + m[15] * w; + + return result; + } + + var vector4 = { + add: add, + angle: angle, + clear: clear, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V4( 1, 0, 0, 0 ), + y: new V4( 0, 1, 0, 0 ), + z: new V4( 0, 0, 1, 0 ), + w: new V4( 0, 0, 0, 1 ), + zero: new V4( 0, 0, 0, 0 ), + one: new V4( 1, 1, 1, 1 ) + }; + + return vector4; + + }; + +}); +define('vector/vector4',['require','../../lib/lodash','common/not-implemented','vector/v4','vector/vector4-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + var vector4 = require( "vector/vector4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector4 = function( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + + this.buffer = new V4( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3, + (arg4 instanceof Vector) ? arg4.buffer : arg4 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + w: { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + + this.modified = true; + this.size = 4; + }; + Vector4.prototype = new Vector(); + Vector4.prototype.constructor = Vector4; + + function add( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.angle( this.buffer, other ); + } + + function clear() { + vector4.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector4( this ); + } + + function distance(arg) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + return vector4.distance(this.buffer, other); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.equal( this.buffer, other ); + } + + function length() { + return vector4.length( this.buffer ); + } + + function limit(max, result) { + result = result || this; + var other; + if( result instanceof Vector4 ) { + other = result.buffer; + result.modified = true; + } else { + other = result; + } + vector4.limit(this.buffer, max, other); + return result; + } + + function multiply( arg, result ) { + result = result || this; + vector4.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector4.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector4.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector4 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + buffer[3] = arg1[3]; + this.modified = true; + } + } else if( 4 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector4.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: distance, + dot: dot, + equal: equal, + length: length, + limit: limit, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector4; + + }; + +}); +define('matrix/m',['require'],function ( require ) { + + var M = function() { + }; + + return M; + +}); +define('matrix/m2',['require','matrix/m'],function ( require ) { + + var M = require( "matrix/m" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var M2 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, + 0, 0]; + } else { + elements = arguments; + } + + var matrix = new FLOAT_ARRAY_TYPE( 4 ); + for( var i = 0; i < 4; ++ i ) { + matrix[i] = elements[i]; + } + + return matrix; + }; + M2.prototype = new M(); + M2.prototype.constructor = M2; + + return M2; + + }; + +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + + + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } + + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } + + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, + + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, + + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, + + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; + + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; + + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; + + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; + + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; + + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; + + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; + + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } + + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } + + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); + + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } + + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } + + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); + + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); + + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); + + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); + + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); + + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); + + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } + + var length = collection.length, + noaccum = arguments.length < 3; + + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; + + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } + + var prop, + props = keys(collection); + + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } + + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); + + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); + + + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); + + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); + + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } + + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } + + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } + + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } + + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } + + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } + + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } + + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } + + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } + + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); + + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } + + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } }; -} + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } + + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } + + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; + } + + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } + + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } + + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } + + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } + + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } + + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } + + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; + + return function() { + var result, + others = arguments; + + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } + + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } + + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { + return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } + + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; + + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } + + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } + + var index = -1, + result = true, + size = 0; + + // add the first collection to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } + + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } + + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } + + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; + + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } + + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); -define('lang',['require'],function ( require ) { + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } - return { - // Simple bind function to maintain "this" for a function. - bind: function bind( obj, func ) { - return function() { - return func.apply( obj, arguments ); - }; - }, + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } + + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } + + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } + + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; + + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; + + // clear stored code snippets + tokenized.length = 0; + + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; + + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } + + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } + + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } + + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } + + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } + + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } - extend: function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } //if - } //for - } //extend + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } + + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; }; -}); + }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; -define('constants',['require'],function ( require ) { + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); - return function() { - - var constants = { - - TAU: 2 * Math.PI, - - PI: Math.PI, - - HALF_PI: Math.PI / 2.0 - - }; - - return constants; - + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; }; - -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + }); -define('vector/vector',['require'],function ( require ) { + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('matrix/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); - return function( FLOAT_ARRAY_TYPE ) { +define('matrix/matrix2-api',['require','common/not-implemented','matrix/m2'],function ( require ) { - var Vector = function( dim, args ) { + return function( FLOAT_ARRAY_TYPE ) { - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); - var vector = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - vector[i] = elements[i]; - } + function add( m1, m2, result ) { + result = result || new M2(); - return vector; + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; - }; + return result; + } - var vector = { - - $: Vector, + function clear( m ) { + m[0] = m[1] = 0; + m[2] = m[3] = 0; - add: function( v1, v2, result ) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] += v1[i] + v2[i]; - } + return m; + } - return result; - }, + function determinant( m ) { + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3]; - clear: function( v ) { - for( var i = 0; i < v.length; ++ i ) { - v[i] = 0; - } - }, + return a00 * a11 - a01 * a10; + } - dot: function( v1, v2 ) { - var res = 0; - for( var i = 0; i < v1.length; ++ i) { - res += v1[i] * v2[i]; - } - return res; - }, + function equal( m1, m2, e ) { + e = e || 0.000001; - equal: function( v1, v2, e ) { - e = e || 0.000001; + if( m1.length !== m2.length ) { + return false; + } - if( v1.length != v2.length ) { - return false; - } + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); - var dim = v1.length; - for( var i = 0; i < dim; ++ i ) { - if ( Math.abs(v1[i] - v2[i]) > e ) { - return false; - } - } + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } - return true; - }, + return true; + } - length: function( v ) { - var va = 0; - for( var i = 0; i < v.length; ++ i ) { - va += v[i] * v[i]; - } + function inverse( m, result ) { + result = result || new M2(); - return Math.sqrt(va); - }, + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3], - multiply: function( v, s, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * s; - } + determinant = a00 * a11 - a01 * a10, + inverseDeterminant; - return result; - }, - - negate: function( v, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * -1; - } - - return result; - }, - - normalize: function( v, result ) { - var len = v.length; - for( var i = 0, abslen = vector.length(v); i < len; ++ i ) { - result[i] = v[i] / abslen; - } + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; - return result; - }, + result[0] = a11 * inverseDeterminant; + result[1] = -a01 * inverseDeterminant; + result[2] = -a10 * inverseDeterminant; + result[3] = a00 * inverseDeterminant; - subtract: function( v1, v2, result) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] = v1[i] - v2[i]; - } + return result; + } - return result; - } + function multiply( m1, m2, result ) { + result = result || new M2(); + + var a00 = m1[0], a01 = m1[1], a10 = m1[2], a11 = m1[3]; + var b00 = m2[0], b01 = m2[1], b10 = m2[2], b11 = m2[3]; - }; - - Object.defineProperty( vector, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + result[0] = a00 * b00 + a01 * b10; + result[1] = a00 * b01 + a01 * b11; + result[2] = a10 * b00 + a11 * b10; + result[3] = a10 * b01 + a11 * b11; - Object.defineProperty( vector, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + return result; + } - Object.defineProperty( vector, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + } + + return m; + } - Object.defineProperty( vector, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function subtract( m1, m2, result ) { + result = result || new M2(); - Object.defineProperty( vector, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; - Object.defineProperty( vector, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + return result; + } - return vector; + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1]; + + result[1] = m[2]; + result[2] = a01; + + return result; + } + + result = result || new M2(); + + result[0] = m[0]; + result[1] = m[2]; + result[2] = m[1]; + result[3] = m[3]; + + return result; + } + var matrix2 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M2( 0, 0, + 0, 0 ), + one: new M2( 1, 1, + 1, 1 ), + identity: new M2( 1, 0, + 0, 1 ) }; + + return matrix2; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/matrix2',['require','../../lib/lodash','common/not-implemented','matrix/m2','matrix/matrix2-api','matrix/matrix'],function ( require ) { -define('vector/vector2',['require','./vector','../constants'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); + var matrix2 = require( "matrix/matrix2-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); - return function( FLOAT_ARRAY_TYPE ) { + function getView( index ) { + return this._views[index]; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); - var constants = require( '../constants' )(); + function getValue( index ) { + return this.buffer[index]; + } - var Vector2 = function() { - if( 0 === arguments.length ) { - return vector.$( 2, [0, 0] ); - } else { - return vector.$( 2, arguments ); - } - }; - - var vector2 = { - - $: Vector2, - - add: function( v1, v2, result ) { - result = result || Vector2(); + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - return vector.add( v1, v2, result ); - }, + function updateViews() { + var i; + for( i = 0; i < 2; ++ i ) { + this._views[i] = new Matrix2View( this, this.buffer, + i*2, (i+1)*2 ); + } + } - angle: function( v1, v2 ) { - var nV1 = Vector2(); - var nV2 = Vector2(); + var Matrix2View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - vector.normalize(v1, nV1); - vector.normalize(v2, nV2); + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); + }; - return Math.acos( vector.dot( nV1, nV2 ) ); - }, + var Matrix2 = function( arg1, arg2, + arg3, arg4 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + this.buffer = new M2( arg1.buffer ); + } else { + this.buffer = new M2( arg1 ); + } + } else if( 4 === argc ) { + this.buffer = new M2( arg1, arg2, + arg3, arg4 ); + } else { + this.buffer = new M2(); + } - clear: vector.clear, + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + } + }); - dot: vector.dot, + this._views = []; - equal: vector.equal, + updateViews.call( this ); - length: vector.length, + this.modified = true; + }; + Matrix2.prototype = new Matrix(); + Matrix2.prototype.constructor = Matrix2; - multiply: function( v, s, result ) { - result = result || Vector2(); + function add( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - return vector.multiply( v, s, result ); - }, - - negate: function( v, result ) { - result = result || Vector2(); - - return vector.negate( v, result ); - }, + result = result || this; + matrix2.add( this.buffer, other, result.buffer ); + result.modified = true; - normalize: function( v, result ) { - result = result || Vector2(); - var len = vector.length(v); + return this; + } - result[0] = v[0]/len; - result[1] = v[1]/len; + function clear() { + matrix2.clear( this.buffer ); + this.modified = true; - return result; - }, - - project: function( v1, v2, result ) { - result = result || Vector2(); - - var dp = v1[0]*v2[0] + v1[1]*v2[1]; - var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); - - result[0] = dp_over_v2_squared_length * v2[0]; - result[1] = dp_over_v2_squared_length * v2[1]; - - return result; - }, - - set: function( v, x, y ) { - v[0] = x; - v[1] = y; - }, - - subtract: function( v1, v2, result ) { - result = result || Vector2(); + return this; + } - return vector.subtract( v1, v2, result ); - } - - }; - - Object.defineProperty( vector2, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + function clone() { + return new Matrix2( this ); + } - Object.defineProperty( vector2, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + function determinant() { + return matrix2.determinant( this.buffer ); + } - Object.defineProperty( vector2, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function equal( arg ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } - Object.defineProperty( vector2, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + return matrix2.equal( this.buffer, other ); + } - Object.defineProperty( vector2, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + function inverse( result ) { + result = result || this; + if( !matrix2.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix2.inverse( this.buffer, result.buffer ); + result.modified = true; - Object.defineProperty( vector2, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix2.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix2.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transpose( result ) { + result = result || this; + matrix2.transpose( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Matrix2.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix2; + + }; + +}); +define('matrix/m3',['require','matrix/m'],function ( require ) { + + var M = require( "matrix/m" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var M3 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, + 0, 0, 0, + 0, 0, 0]; + } else { + elements = arguments; + } - return vector2; + var matrix = new FLOAT_ARRAY_TYPE( 9 ); + for( var i = 0; i < 9; ++ i ) { + matrix[i] = elements[i]; + } + return matrix; }; + M3.prototype = new M(); + M3.prototype.constructor = M3; + + return M3; + + }; + +}); +define('matrix/matrix3-api',['require','common/not-implemented','matrix/m3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M3(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; + + return result; + } + + function clear( m ) { + m[0] = m[1] = m[2] = 0; + m[3] = m[4] = m[5] = 0; + m[6] = m[7] = m[8] = 0; + + return m; + } + + function determinant( m ) { + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + } + + function equal( m1, m2, e ) { + e = e || 0.000001; + + if( m1.length !== m2.length ) { + return false; + } + + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e ) { + return false; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + return true; + } -define('vector/vector3',['require','./vector'],function ( require ) { + function inverse( m, result ) { + result = result || new M3(); - return function( FLOAT_ARRAY_TYPE ) { + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8], - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, - var Vector3 = function() { - if( 0 === arguments.length ) { - return vector.$( 3, [0, 0, 0] ); - } else { - return vector.$( 3, arguments ); - } - }; + determinant = a00 * b01 + a01 * b11 + a02 * b21, + inverseDeterminant; - var vector3 = { - - $: Vector3, + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; - add: function( v1, v2, result ) { - result = result || Vector3(); + result[0] = b01 * inverseDeterminant; + result[1] = (-a22 * a01 + a02 * a21) * inverseDeterminant; + result[2] = (a12 * a01 - a02 * a11) * inverseDeterminant; + result[3] = b11 * inverseDeterminant; + result[4] = (a22 * a00 - a02 * a20) * inverseDeterminant; + result[5] = (-a12 * a00 + a02 * a10) * inverseDeterminant; + result[6] = b21 * inverseDeterminant; + result[7] = (-a21 * a00 + a01 * a20) * inverseDeterminant; + result[8] = (a11 * a00 - a01 * a10) * inverseDeterminant; - return vector.add( v1, v2, result ); - }, + return result; + } - angle: function( v1, v2 ) { + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L682 + function multiply( m1, m2, result ) { + result = result || new M3(); + + // Cache the matrix values (makes for huge speed increases!) + var a00 = m1[0], a01 = m1[1], a02 = m1[2], + a10 = m1[3], a11 = m1[4], a12 = m1[5], + a20 = m1[6], a21 = m1[7], a22 = m1[8], + + b00 = m2[0], b01 = m2[1], b02 = m2[2], + b10 = m2[3], b11 = m2[4], b12 = m2[5], + b20 = m2[6], b21 = m2[7], b22 = m2[8]; + + result[0] = a00 * b00 + a01 * b10 + a02 * b20; + result[1] = a00 * b01 + a01 * b11 + a02 * b21; + result[2] = a00 * b02 + a01 * b12 + a02 * b22; + + result[3] = a10 * b00 + a11 * b10 + a12 * b20; + result[4] = a10 * b01 + a11 * b11 + a12 * b21; + result[5] = a10 * b02 + a11 * b12 + a12 * b22; + + result[6] = a20 * b00 + a21 * b10 + a22 * b20; + result[7] = a20 * b01 + a21 * b11 + a22 * b21; + result[8] = a20 * b02 + a21 * b12 + a22 * a22; + + return result; + } + + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + } + + return m; + } - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2])) - ); - }, + function subtract( m1, m2, result ) { + result = result || new M3(); - clear: vector.clear, + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; - cross: function( v1, v2, result ) { - result = result || Vector3(); + return result; + } - result[0] = (v1[1] * v2[2]) - (v2[1] * v1[2]); - result[1] = (v1[2] * v2[0]) - (v2[2] * v1[0]); - result[2] = (v1[0] * v2[1]) - (v2[0] * v1[1]); + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], + a12 = m[5]; - return result; - }, + result[1] = m[3]; + result[2] = m[6]; + result[3] = a01; + result[5] = m[7]; + result[6] = a02; + result[7] = a12; - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - }, + return result; + } - equal: vector.equal, + result = result || new M3(); - length: vector.length, + result[0] = m[0]; + result[1] = m[3]; + result[2] = m[6]; + result[3] = m[1]; + result[4] = m[4]; + result[5] = m[7]; + result[6] = m[2]; + result[7] = m[5]; + result[8] = m[8]; - multiply: function( v, s, result ) { - result = result || Vector3(); + return result; + } - return vector.multiply( v, s, result ); - }, + var matrix3 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M3( 0, 0, 0, + 0, 0, 0, + 0, 0, 0 ), + one: new M3( 1, 1, 1, + 1, 1, 1, + 1, 1, 1 ), + identity: new M3( 1, 0, 0, + 0, 1, 0, + 0, 0, 1 ) + }; + + return matrix3; - normal: function( v1, v2, result ) { - result = result || Vector3(); + }; - return Vector3.cross( v1, v2, result ); - }, +}); +define('matrix/matrix3',['require','../../lib/lodash','common/not-implemented','matrix/m3','matrix/matrix3-api','matrix/matrix'],function ( require ) { - normalize: function( v, result ) { - result = result || Vector3(); - var len = vector.length(v); + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); + var matrix3 = require( "matrix/matrix3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; + function getView( index ) { + return this._views[index]; + } - return result; - }, - - set: function( v, x, y, z ) { - v[0] = x; - v[1] = y; - v[2] = z; - }, + function getValue( index ) { + return this.buffer[index]; + } - subtract: function( v1, v2, result ) { - result = result || Vector3(); + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - return vector.subtract( v1, v2, result ); - } + function updateViews() { + var i; + for( i = 0; i < 3; ++ i ) { + this._views[i] = new Matrix3View( this, this.buffer, + i*3, (i+1)*3 ); + } + } - }; - - Object.defineProperty( vector3, 'x', { - get: function() { - return Vector3( [1, 0, 0] ); - }, - enumerable: true - }); + var Matrix3View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - Object.defineProperty( vector3, 'y', { - get: function() { - return Vector3( [0, 1, 0] ); - }, - enumerable: true - }); + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + }; - Object.defineProperty( vector3, 'z', { - get: function() { - return Vector3( [0, 0, 1] ); - }, - enumerable: true - }); + var Matrix3 = function( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + this.buffer = new M3( arg1.buffer ); + } else { + this.buffer = new M3( arg1 ); + } + } else if( 9 === argc ) { + this.buffer = new M3( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ); + } else { + this.buffer = new M3(); + } - Object.defineProperty( vector3, 'zero', { - get: function() { - return Vector3( [0, 0, 0] ); - }, - enumerable: true - }); + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + } + }); - Object.defineProperty( vector3, 'one', { - get: function() { - return Vector3( [1, 1, 1] ); - }, - enumerable: true - }); + this._views = []; - return vector3; + updateViews.call( this ); + this.modified = true; }; + Matrix3.prototype = new Matrix(); + Matrix3.prototype.constructor = Matrix3; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function add( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } -define('vector/vector4',['require','./vector'],function ( require ) { + result = result || this; + matrix3.add( this.buffer, other, result.buffer ); + result.modified = true; - return function( FLOAT_ARRAY_TYPE ) { + return this; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + function clear() { + matrix3.clear( this.buffer ); + this.modified = true; - var Vector4 = function() { - if( 0 === arguments.length ) { - return vector.$( 4, [0, 0, 0, 0] ); - } else { - return vector.$( 4, arguments ); - } - }; + return this; + } - var vector4 = { - - $: Vector4, + function clone() { + return new Matrix3( this ); + } - add: function( v1, v2, result ) { - result = result || Vector4(); + function determinant() { + return matrix3.determinant( this.buffer ); + } - result[0] = v1[0] + v2[0]; - result[1] = v1[1] + v2[1]; - result[2] = v1[2] + v2[2]; - result[3] = v1[3] + v2[3]; + function equal( arg ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - return result; - }, + return matrix3.equal( this.buffer, other ); + } - // Computes the angle between v1 and v2 - angle: function( v1, v2 ) { - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3])) - ); - }, + function inverse( result ) { + result = result || this; + if( !matrix3.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix3.inverse( this.buffer, result.buffer ); + result.modified = true; - clear: vector.clear, + return this; + } - // Computes the dot product of v1 and v2 - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; - }, + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - equal: vector.equal, + result = result || this; + matrix3.multiply( this.buffer, other, result.buffer ); + result.modified = true; - length: vector.length, + return this; + } - // Computes v * s - multiply: function( v, s, result ) { - result = result || Vector4(); + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } - return vector.multiply( v, s, result ); - }, + return this; + } - // Computes a Vector4 with same direction as v having unit length - normalize: function( v, result ) { - result = result || Vector4(); - var len = vector.length(v); + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; - result[3] = v[3]/len; + result = result || this; + matrix3.subtract( this.buffer, other, result.buffer ); + result.modified = true; - return result; - }, - - set: function( v, x, y, z, w ) { - v[0] = x; - v[1] = y; - v[2] = z; - v[3] = w; - }, - - // Computes v1 - v2 - subtract: function( v1, v2, result ) { - result = result || Vector4(); - - return vector.subtract( v1, v2, result ); - } + return this; + } - } - - Object.defineProperty( vector4, 'x', { - get: function() { - return Vector4( [1, 0, 0, 0] ); - }, - enumerable: true - }); + function transpose( result ) { + result = result || this; + matrix3.transpose( this.buffer, result.buffer ); + result.modified = true; - Object.defineProperty( vector4, 'y', { - get: function() { - return Vector4( [0, 1, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'z', { - get: function() { - return Vector4( [0, 0, 1, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'w', { - get: function() { - return Vector4( [0, 0, 0, 1] ); - }, - enumerable: true - }); + return this; + } + + _.extend( Matrix3.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix3; - Object.defineProperty( vector4, 'zero', { - get: function() { - return Vector4( [0, 0, 0, 0] ); - }, - enumerable: true - }); + }; - Object.defineProperty( vector4, 'one', { - get: function() { - return Vector4( [1, 1, 1, 1] ); - }, - enumerable: true - }); +}); +define('matrix/m4',['require','matrix/m'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var M = require( "matrix/m" ); + + var M4 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0]; + } else { + elements = arguments; + } - return vector4; + var matrix = new FLOAT_ARRAY_TYPE( 16 ); + for( var i = 0; i < 16; ++ i ) { + matrix[i] = elements[i]; + } + return matrix; }; + M4.prototype = new M(); + M4.prototype.constructor = M4; + + return M4; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/matrix4-api',['require','common/not-implemented','matrix/m4','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; + result[9] = m1[9] + m2[9]; + result[10] = m1[10] + m2[10]; + result[11] = m1[11] + m2[11]; + result[12] = m1[12] + m2[12]; + result[13] = m1[13] + m2[13]; + result[14] = m1[14] + m2[14]; + result[15] = m1[15] + m2[15]; + + return result; + } -define('vector/quaternion',['require','./vector4','./vector3'],function ( require ) { + function clear( m ) { + m[0] = m[1] = m[2] = m[3] = 0; + m[4] = m[5] = m[6] = m[7] = 0; + m[8] = m[9] = m[10] = m[11] = 0; + m[12] = m[13] = m[14] = m[15] = 0; - return function( FLOAT_ARRAY_TYPE ) { + return m; + } - var vector4 = require( './vector4' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( './vector3' )( FLOAT_ARRAY_TYPE ); + function determinant( m ) { + var a0 = m[0] * m[5] - m[1] * m[4]; + var a1 = m[0] * m[6] - m[2] * m[4]; + var a2 = m[0] * m[7] - m[3] * m[4]; + var a3 = m[1] * m[6] - m[2] * m[5]; + var a4 = m[1] * m[7] - m[3] * m[5]; + var a5 = m[2] * m[7] - m[3] * m[6]; + var b0 = m[8] * m[13] - m[9] * m[12]; + var b1 = m[8] * m[14] - m[10] * m[12]; + var b2 = m[8] * m[15] - m[11] * m[12]; + var b3 = m[9] * m[14] - m[10] * m[13]; + var b4 = m[9] * m[15] - m[11] * m[13]; + var b5 = m[10] * m[15] - m[11] * m[14]; - var Quaternion = vector4.$; + return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; + } - var quaternion = { + function equal( m1, m2, e ) { + e = e || 0.000001; - $: Quaternion, + if( m1.length !== m2.length ) { + return false; + } - to: { - rpy: function( q, result ) { - var r = result || vector3.$(); - var atan2 = Math.atan2, - asin = Math.asin; + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + var d9 = Math.abs( m1[9] - m2[9] ); + var d10 = Math.abs( m1[10] - m2[10] ); + var d11 = Math.abs( m1[11] - m2[11] ); + var d12 = Math.abs( m1[12] - m2[12] ); + var d13 = Math.abs( m1[13] - m2[13] ); + var d14 = Math.abs( m1[14] - m2[14] ); + var d15 = Math.abs( m1[15] - m2[15] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e || + isNaN( d9 ) || d9 > e || + isNaN( d10 ) || d10 > e || + isNaN( d11 ) || d11 > e || + isNaN( d12 ) || d12 > e || + isNaN( d13 ) || d13 > e || + isNaN( d14 ) || d14 > e || + isNaN( d15 ) || d15 > e ) { + return false; + } - r[0] = atan2( 2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] + 2*q[2]*q[2] ); - r[1] = asin( 2*q[0]*q[2] - 2*q[3]*q[1] ); - r[2] = atan2( 2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] + 2*q[3]*q[3] ); + return true; + } - if( !result ) { - return r; - } - } - }, - - from: { - rpy: function( v, result ) { - var r = result || quaternion.$(); - var sin = Math.sin, - cos = Math.cos; - var half_phi = v[0] / 2, - half_theta = v[1] / 2, - half_psi = v[2] / 2; - var sin_half_phi = sin( half_phi ), - cos_half_phi = cos( half_phi ), - sin_half_theta = sin( half_theta ), - cos_half_theta = cos( half_theta ), - sin_half_psi = sin( half_psi ), - cos_half_psi = cos( half_psi ); - - r[0] = cos_half_phi * cos_half_theta * cos_half_psi + - sin_half_phi * sin_half_theta * sin_half_psi; - r[1] = sin_half_phi * cos_half_theta * cos_half_psi - - cos_half_phi * sin_half_theta * sin_half_psi; - r[2] = cos_half_phi * sin_half_theta * cos_half_psi + - sin_half_phi * cos_half_theta * sin_half_psi; - r[3] = cos_half_phi * cos_half_theta * sin_half_psi - - sin_half_phi * sin_half_theta * cos_half_psi; - - if( !result ) { - return r; - } - } - }, + function inverse( m, result ) { + result = result || new M4(); + + var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], + a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], + a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], + a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + determinant = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), + inverseDeterminant; + + // Determinant, throw exception if singular + if( !determinant ) { + return undefined; + } + + inverseDeterminant = 1 / determinant; + + result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * inverseDeterminant; + result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * inverseDeterminant; + result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * inverseDeterminant; + result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * inverseDeterminant; + result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * inverseDeterminant; + result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * inverseDeterminant; + result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * inverseDeterminant; + result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * inverseDeterminant; + result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * inverseDeterminant; + result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * inverseDeterminant; + result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * inverseDeterminant; + result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * inverseDeterminant; + result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * inverseDeterminant; + result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * inverseDeterminant; + result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * inverseDeterminant; + result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * inverseDeterminant; + + return result; + } + + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L1295 + function multiply( m1, m2, result ) { + result = result || new M4(); + + var a00 = m1[0], a01 = m1[1], a02 = m1[2], a03 = m1[3], + a10 = m1[4], a11 = m1[5], a12 = m1[6], a13 = m1[7], + a20 = m1[8], a21 = m1[9], a22 = m1[10], a23 = m1[11], + a30 = m1[12], a31 = m1[13], a32 = m1[14], a33 = m1[15], + + b00 = m2[0], b01 = m2[1], b02 = m2[2], b03 = m2[3], + b10 = m2[4], b11 = m2[5], b12 = m2[6], b13 = m2[7], + b20 = m2[8], b21 = m2[9], b22 = m2[10], b23 = m2[11], + b30 = m2[12], b31 = m2[13], b32 = m2[14], b33 = m2[15]; + + result[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; + result[1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; + result[2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; + result[3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; + result[4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; + result[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; + result[6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; + result[7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; + result[8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; + result[9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; + result[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; + result[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; + result[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; + result[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; + result[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; + result[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; - length: vector4.length, + return result; + } - multiply: function( q1, q2, result ) { - var r = result || quaternion.$(); + function multiplyV3( m, v, result ) { + result = result || new V3(); - r[0] = q1[3] * q2[0] + q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1]; // x - r[1] = q1[3] * q2[1] - q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0]; // y - r[2] = q1[3] * q2[2] + q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3]; // z - r[3] = q1[3] * q2[3] - q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2]; // w + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + result[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + result[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - if( !result ) { - return r; - } - }, + return result; + } - normalize: vector4.normalize + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + m[9] = values[9]; + m[10] = values[10]; + m[11] = values[11]; + m[12] = values[12]; + m[13] = values[13]; + m[14] = values[14]; + m[15] = values[15]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + m[9] = arguments[10]; + m[10] = arguments[11]; + m[11] = arguments[12]; + m[12] = arguments[13]; + m[13] = arguments[14]; + m[14] = arguments[15]; + m[15] = arguments[16]; + } + + return m; + } + + function subtract( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; + result[9] = m1[9] - m2[9]; + result[10] = m1[10] - m2[10]; + result[11] = m1[11] - m2[11]; + result[12] = m1[12] - m2[12]; + result[13] = m1[13] - m2[13]; + result[14] = m1[14] - m2[14]; + result[15] = m1[15] - m2[15]; - }; + return result; + } - Object.defineProperty( quaternion, 'identity', { - get: function() { - return Quaternion( [0, 0, 0, 1] ); - }, - enumerable: true - }); + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], a03 = m[3], + a12 = m[6], a13 = m[7], + a23 = m[11]; + + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = a01; + result[6] = m[9]; + result[7] = m[13]; + result[8] = a02; + result[9] = a12; + result[11] = m[14]; + result[12] = a03; + result[13] = a13; + result[14] = a23; + + return result; + } - return quaternion; + result = result || new M4(); + + result[0] = m[0]; + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = m[1]; + result[5] = m[5]; + result[6] = m[9]; + result[7] = m[13]; + result[8] = m[2]; + result[9] = m[6]; + result[10] = m[10]; + result[11] = m[14]; + result[12] = m[3]; + result[13] = m[7]; + result[14] = m[11]; + result[15] = m[15]; + return result; + } + + var matrix4 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M4( 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 ), + one: new M4( 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1 ), + identity: new M4( 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ) }; + + return matrix4; + + }; }); +define('matrix/matrix4',['require','../../lib/lodash','common/not-implemented','matrix/m4','matrix/matrix4-api','matrix/matrix'],function ( require ) { -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); -define('matrix/matrix',['require'],function ( require ) { + function getView( index ) { + return this._views[index]; + } - return function( FLOAT_ARRAY_TYPE ) { - - var Matrix = function( dim, args ) { - - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } - - var matrix = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - matrix[i] = elements[i]; - } - - return matrix; - - }; - - var matrix = { - - $: Matrix, - - add: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - result[i] += m1[i] + m2[i]; - } + function getValue( index ) { + return this.buffer[index]; + } - return result; - }, - - subtract: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - m1[i] -= m2[i]; - } - return m1; - }, - - clear: function( m ) { - for( var i = 0; i < m.length; ++ i ) { - m[i] = 0; - } - }, - - equal: function( m1, m2, e ) { - e = e || 0.000001; + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - if( m1.length != m2.length ) { - return false; - } - - var dim = m1.length; - for( var i = 0; i < dim; ++ i ) { - if( Math.abs( m1[i] - m2[i] ) > e ) { - return false; - } - } + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new Matrix4View( this, this.buffer, + i*4, (i+1)*4 ); + } + } - return true; - } - - }; - - return matrix; - + var Matrix4View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); + + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); }; - -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var Matrix4 = function( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else { + this.buffer = new M4( arg1 ); + } + } else if( 16 === argc ) { + this.buffer = new M4( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ); + } else { + this.buffer = new M4(); + } + + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); -define('matrix/matrix2',['require','./matrix'],function ( require ) { + this._views = []; - return function( FLOAT_ARRAY_TYPE ) { + updateViews.call( this ); - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + this.modified = true; + }; + Matrix4.prototype = new Matrix(); + Matrix4.prototype.constructor = Matrix4; - var Matrix2 = function() { - if( 0 === arguments.length ) { - return matrix.$( 4, [0, 0, - 0, 0] ); - } else { - return matrix.$( 4, arguments ); - } - }; + function add( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - var matrix2 = { + result = result || this; + matrix4.add( this.buffer, other, result.buffer ); + result.modified = true; - $: Matrix2, + return this; + } - add: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + function clear() { + matrix4.clear( this.buffer ); + this.modified = true; - subtract: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) { - result = temp; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + return this; + } - clear: matrix.clear, + function clone() { + return new Matrix4( this ); + } - equal: matrix.equal, + function determinant() { + return matrix4.determinant( this.buffer ); + } - determinant: function( m ) { - return m[0]*m[3] - m[1]*m[2]; - }, - - inverse: function( m, result ) { - - var det = matrix2.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix2(); - - result[0] = m[3]/det; - result[1] = m[1]*-1/det; - result[2] = m[2]*-1/det; - result[3] = m[0]/det; - - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix2(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][2]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][3]; - result[2] = temp[2]*ml[i][0] + temp[3]*ml[i][2]; - result[3] = temp[2]*ml[i][1] + temp[3]*ml[i][3]; - temp = result; - } - } - return result; - }, - - transpose: function( m, result ) { - result = result || Matrix2(); - - var temp = m[1]; - result[0] = m[0]; - result[1] = m[2]; - result[2] = temp; - result[3] = m[3]; - - return result; - } + function equal( arg ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix4.equal( this.buffer, other ); + } + + function inverse( result ) { + result = result || this; + if( !matrix4.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix4.inverse( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + other = arg1.buffer; + } else { + other = arg1; } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - Object.defineProperty( matrix2, 'zero', { - get: function() { - return Matrix2( [0, 0, - 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'one', { - get: function() { - return Matrix2( [1, 1, - 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'identity', { - get: function() { - return Matrix2( [1, 0, - 0, 1] ); - }, - enumerable: true - }); + result = result || this; + matrix4.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } - return matrix2; + function transpose( result ) { + result = result || this; + matrix4.transpose( this.buffer, result.buffer ); + result.modified = true; - }; + return this; + } + + _.extend( Matrix4.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix4; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/transform-api',['require','common/not-implemented','matrix/m4','matrix/matrix4-api'],function ( require ) { -define('matrix/matrix3',['require','./matrix'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { - return function( FLOAT_ARRAY_TYPE ) { + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + function compound( transform, t, r, s ) { + transform = transform || new M4(matrix4.identity); - var Matrix3 = function() { - if( 0 === arguments.length ) { - return matrix.$( 9, [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - } else { - return matrix.$( 9, arguments ); - } - }; + if( t ) { + translate( t, transform ); + } - var matrix3 = { - - $: Matrix3, + if( r ) { + rotate( r, transform ); + } - add: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + if( s ) { + scale( s, transform ); + } - subtract: function( ml, result ) { - result = result || Matrix3(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + return transform; + } - clear: matrix.clear, + function set(transform, t, r, s){ + if (transform){ + matrix4.set(transform, matrix4.identity); + } + return compound(transform, t, r, s); + } - equal: matrix.equal, + function rotate( v, result ) { + result = result || new M4( matrix4.identity ); - determinant: function( m ) { + var sinA, + cosA; + var rotation; - return m[0]*(m[4]*m[8] - m[5]*m[7]) - - m[1]*(m[3]*m[8] - m[5]*m[6]) - + m[2]*(m[3]*m[7] - m[4]*m[6]); - }, - - inverse: function( m, result ) { - var det = matrix3.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix3(); - - result[0] = (m[8]*m[4] - m[7]*m[5])/det; - result[1] = -(m[8]*m[1] - m[7]*[2])/det; - result[2] = (m[5]*m[1] - m[4]*m[2])/det; - - result[3] = -(m[8]*m[3] - m[6]*m[5])/det; - result[4] = (m[8]*m[0] - m[6]*m[2])/det; - result[5] = -(m[5]*m[0] - m[3]*m[2])/det; - - result[6] = (m[7]*m[3] - m[6]*m[4])/det; - result[7] = -(m[7]*m[0] - m[6]*m[1])/det; - result[8] = (m[4]*m[0] - m[3]*m[1])/det; + if( 0 !== v[2] ) { + sinA = Math.sin( v[2] ); + cosA = Math.cos( v[2] ); - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][3] + temp[2]*ml[i][6]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][4] + temp[2]*ml[i][7]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][5] + temp[2]*ml[i][8]; - - result[3] = temp[3]*ml[i][0] + temp[4]*ml[i][3] + temp[5]*ml[i][6]; - result[4] = temp[3]*ml[i][1] + temp[4]*ml[i][4] + temp[5]*ml[i][7]; - result[5] = temp[3]*ml[i][2] + temp[4]*ml[i][5] + temp[5]*ml[i][8]; - - result[6] = temp[6]*ml[i][0] + temp[7]*ml[i][3] + temp[8]*ml[i][6]; - result[7] = temp[6]*ml[i][1] + temp[7]*ml[i][4] + temp[8]*ml[i][7]; - result[8] = temp[6]*ml[i][2] + temp[7]*ml[i][5] + temp[8]*ml[i][8]; - - temp = result; - } - } - return result; - }, - - // Convert a vector rotation (in radians) to a 3x3 matrix - rotate: function( v, result ) { - var r = result || matrix3.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix3.$([ cosA, sinA, 0, - -sinA, cosA, 0, - 0, 0, 1 ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + rotation = [ cosA, -sinA, 0, 0, + sinA, cosA, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix3.$([ cosA, 0, -sinA, - 0, 1, 0, - sinA, 0, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + if( 0 !== v[1] ) { + sinA = Math.sin( v[1] ); + cosA = Math.cos( v[1] ); - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix3.$([ 1, 0, 0, - 0, cosA, sinA, - 0, -sinA, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + rotation = [ cosA, 0, sinA, 0, + 0, 1, 0, 0, + -sinA, 0, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - if( !result ) { - return r; - } - }, + if( 0 !== v[0] ) { + sinA = Math.sin( v[0] ); + cosA = Math.cos( v[0] ); + + rotation = [ 1, 0, 0, 0, + 0, cosA, -sinA, 0, + 0, sinA, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } + + return result; + } - transpose: function( m, result ) { - result = result || Matrix3(); + function scale( v, result ) { + result = result || new M4( matrix4.identity ); - var a01 = m[1], a02 = m[2], a12 = m[5]; - - result[0] = m[0]; - result[1] = m[3]; - result[2] = m[6]; - result[3] = a01; - result[4] = m[4]; - result[5] = m[7]; - result[6] = a02; - result[7] = a12; - result[8] = m[8]; - - return result; - } + matrix4.multiply( result, [v[0], 0, 0, 0, + 0, v[1], 0, 0, + 0, 0, v[2], 0, + 0, 0, 0, 1], result ); - }; - - Object.defineProperty( matrix3, 'zero', { - get: function() { - return Matrix3( [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix3, 'one', { - get: function() { - return Matrix3( [1, 1, 1, - 1, 1, 1, - 1, 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix3, 'identity', { - get: function() { - return Matrix3( [1, 0, 0, - 0, 1, 0, - 0, 0, 1] ); - }, - enumerable: true - }); + return result; + } + + function translate( v, result ) { + result = result || new M4( matrix4.identity ); - return matrix3; + matrix4.multiply( result, [1, 0, 0, v[0], + 0, 1, 0, v[1], + 0, 0, 1, v[2], + 0, 0, 0, 1], result ); + return result; + } + + var transform = { + compound: compound, + set: set, + rotate: rotate, + scale: scale, + translate: translate }; + return transform; + + }; + }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/t',['require','matrix/m','matrix/m4','matrix/transform-api'],function ( require ) { -define('matrix/matrix4',['require','./matrix','../vector/vector3'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { - return function( FLOAT_ARRAY_TYPE ) { + var M = require( "matrix/m" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require("matrix/transform-api")( FLOAT_ARRAY_TYPE ); - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( '../vector/vector3' )( FLOAT_ARRAY_TYPE ); + var T = function(t, r, s) { + var matrix = new M4(); + return transform.set(matrix, t, r, s); + }; + T.prototype = new M(); + T.prototype.constructor = T; - var Matrix4 = function() { - if( 0 === arguments.length ) { - return matrix.$( 16, [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - } else { - return matrix.$( 16, arguments ); - } - }; + return T; - var matrix4 = { - - $: Matrix4, + }; - add: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, - - subtract: function( ml, result ) { - result = result || Matrix4(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, +}); +define('matrix/transform',['require','common/not-implemented','matrix/m4','matrix/transform-api','matrix/matrix4-api','matrix/matrix4'],function ( require ) { - clear: matrix.clear, + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require( "matrix/transform-api" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix4 = require( "matrix/matrix4" )( FLOAT_ARRAY_TYPE ); - equal: matrix.equal, + function getView( index ) { + return this._views[index]; + } - multiply: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][4] + temp[2]*ml[i][8] + temp[3]*ml[i][12]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][5] + temp[2]*ml[i][9] + temp[3]*ml[i][13]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][6] + temp[2]*ml[i][10] + temp[3]*ml[i][14]; - result[3] = temp[0]*ml[i][3] + temp[1]*ml[i][7] + temp[2]*ml[i][11] + temp[3]*ml[i][15]; - result[4] = temp[4]*ml[i][0] + temp[5]*ml[i][4] + temp[6]*ml[i][8] + temp[7]*ml[i][12]; - result[5] = temp[4]*ml[i][1] + temp[5]*ml[i][5] + temp[6]*ml[i][9] + temp[7]*ml[i][13]; - result[6] = temp[4]*ml[i][2] + temp[5]*ml[i][6] + temp[6]*ml[i][10] + temp[7]*ml[i][14]; - result[7] = temp[4]*ml[i][3] + temp[5]*ml[i][7] + temp[6]*ml[i][11] + temp[7]*ml[i][15]; - result[8] = temp[8]*ml[i][0] + temp[9]*ml[i][4] + temp[10]*ml[i][8] + temp[11]*ml[i][12]; - result[9] = temp[8]*ml[i][1] + temp[9]*ml[i][5] + temp[10]*ml[i][9] + temp[11]*ml[i][13]; - result[10] = temp[8]*ml[i][2] + temp[9]*ml[i][6] + temp[10]*ml[i][10] + temp[11]*ml[i][14]; - result[11] = temp[8]*ml[i][3] + temp[9]*ml[i][7] + temp[10]*ml[i][11] + temp[11]*ml[i][15]; - result[12] = temp[12]*ml[i][0] + temp[13]*ml[i][4] + temp[14]*ml[i][8] + temp[15]*ml[i][12]; - result[13] = temp[12]*ml[i][1] + temp[13]*ml[i][5] + temp[14]*ml[i][9] + temp[15]*ml[i][13]; - result[14] = temp[12]*ml[i][2] + temp[13]*ml[i][6] + temp[14]*ml[i][10] + temp[15]*ml[i][14]; - result[15] = temp[12]*ml[i][3] + temp[13]*ml[i][7] + temp[14]*ml[i][11] + temp[15]*ml[i][15]; - - temp = result; - } - } - return result; - }, - - multiplyVector3: function( m, v, result ) { - result = result || vector3.$(); - - result[0] = m[0] * v[0] + m[2] * v[1] + m[3] * v[2]; - result[1] = m[4] * v[0] + m[5] * v[1] + m[6] * v[2]; - result[2] = m[8] * v[0] + m[9] * v[1] + m[10] * v[2]; + function getValue( index ) { + return this.buffer[index]; + } - return result; - }, + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - determinant: function (m) { - var a0 = m[0] * m[5] - m[1] * m[4]; - var a1 = m[0] * m[6] - m[2] * m[4]; - var a2 = m[0] * m[7] - m[3] * m[4]; - var a3 = m[1] * m[6] - m[2] * m[5]; - var a4 = m[1] * m[7] - m[3] * m[5]; - var a5 = m[2] * m[7] - m[3] * m[6]; - var b0 = m[8] * m[13] - m[9] * m[12]; - var b1 = m[8] * m[14] - m[10] * m[12]; - var b2 = m[8] * m[15] - m[11] * m[12]; - var b3 = m[9] * m[14] - m[10] * m[13]; - var b4 = m[9] * m[15] - m[11] * m[13]; - var b5 = m[10] * m[15] - m[11] * m[14]; - - var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; - - return det; - }, + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new TransformView( this, this.buffer, + i*4, (i+1)*4 ); + } + } - transpose: function (m , result) { - result = result || Matrix4(); - - result[0] = m[0]; - result[1] = m[4]; - result[2] = m[8]; - result[3] = m[12]; - result[4] = m[1]; - result[5] = m[5]; - result[6] = m[9]; - result[7] = m[13]; - result[8] = m[2]; - result[9] = m[6]; - result[10] = m[10]; - result[11] = m[14]; - result[12] = m[3]; - result[13] = m[7]; - result[14] = m[11]; - result[15] = m[15]; - - return result; - }, + var TransformView = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - inverse: function (m, result) { - - result = result || Matrix4(); - - var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], - a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], - a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], - a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), - invDet; - - // Determinant, throw exception if singular - if (!d) - throw 'matrix is singular'; - - invDet = 1 / d; - result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; - result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; - result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; - result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; - result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; - result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; - result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; - result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; - result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; - result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; - result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; - result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; - result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; - result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; - result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; - result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; - - return result; - }, + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + }; - toHTML: function( m ) { - var result = "[ "; - for( var i = 0; i < 4; ++ i ) { - result += "
    "; - for( var j = 0; j < 4; ++ j ) { - result += " (" + m[4*i+j] + ") "; - } - } - result += " ]"; - return result; - } + var Transform = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + if( 0 === argc ) { + this.buffer = new M4(matrix4.identity); + }else if( 1 === argc ) { + if( arg1 instanceof Transform || + arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else if( arg1 instanceof M4 ) { + this.buffer = new M4( arg1 ); + } else { + this.buffer = new M4(matrix4.identity); + transform.compound( this.buffer, arg1, arg2, arg3 ); + } + } else { + this.buffer = new M4(matrix4.identity); + transform.compound(this.buffer, arg1, arg2, arg3 ); + } - }; - Object.defineProperty( matrix4, 'zero', { - get: function() { - return Matrix4( [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'one', { - get: function() { - return Matrix4( [1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'identity', { - get: function() { - return Matrix4( [1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1] ); - }, - enumerable: true - }); + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); + + this._views = []; - return matrix4; + updateViews.call( this ); + this.modified = true; }; -}); + function clone() { + return new Transform( this ); + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, -nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function equal( arg ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } -define('matrix/transform',['require','./matrix4'],function ( require ) { + return matrix4.equal( this.buffer, other ); + } - return function( FLOAT_ARRAY_TYPE ) { + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } - var matrix4 = require( './matrix4' )( FLOAT_ARRAY_TYPE ); + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; - var Transform = matrix4.$; - - var transform = { - - $: Transform, + return this; + } - fixed: function( vt, vr, vs ) { - var r = matrix4.identity; + function rotate( v, result ) { + var rotation = transform.rotate( v ); - if( vt ) { - transform.translate( vt, r ); - } + result = result || this; + matrix4.multiply( this.buffer, rotation, result.buffer ); + result.modified = true; - if( vr ) { - transform.rotate( vr, r ); - } + return this; + } - if( vs ) { - transform.scale( vs, r ); - } + function scale( v, result ) { + var scaled = transform.scale( v ); - return r; - }, + result = result || this; + matrix4.multiply( this.buffer, scaled, result.buffer ); + result.modified = true; - // Convert a vector rotation (in radians) to a 4x4 matrix - rotate: function( v, result ) { - var r = result || matrix4.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix4.$([ cosA, sinA, 0, 0, - -sinA, cosA, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + return this; + } - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix4.$([ cosA, 0, -sinA, 0, - 0, 1, 0, 0, - sinA, 0, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function set( t, r, s ) { + transform.set( this.buffer, t, r, s ); + this.modified = true; + } - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix4.$([ 1, 0, 0, 0, - 0, cosA, sinA, 0, - 0, -sinA, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function transformDirection( v, result ) { - if( !result ) { - return r; - } - }, + } - // Convert a vector3 scale to a 4x4 matrix - scale: function( v, result ) { - var r = [ v[0], 0.0, 0.0, 0.0, - 0.0, v[1], 0.0, 0.0, - 0.0, 0.0, v[2], 0.0, - 0.0, 0.0, 0.0, 1.0 ]; + function transformPoint( v, result ) { - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - }, + } - // Convert a vector3 translation to a 4x4 matrix - translate: function( v, result ) { - var r = [ 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - v[0], v[1], v[2], 1.0 ] + function translate( v, result ) { + var translation = transform.translate( v ); - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - } + result = result || this; + matrix4.multiply( this.buffer, translation, result.buffer ); + result.modified = true; - }; - - return transform; + return this; + } + Transform.prototype = { + clone: clone, + equal: equal, + inverseTransformDirection: notImplemented, + inverseTransformPoint: notImplemented, + multiply: multiply, + rotate: rotate, + scale: scale, + set: set, + transformDirection: notImplemented, + transformPoint: notImplemented, + translate: translate }; + return Transform; + + }; + }); +define('_math',['require','constants','equal','vector/v2','vector/vector2','vector/vector2-api','vector/v3','vector/vector3','vector/vector3-api','vector/v4','vector/vector4','vector/vector4-api','matrix/m2','matrix/matrix2','matrix/matrix2-api','matrix/m3','matrix/matrix3','matrix/matrix3-api','matrix/m4','matrix/matrix4','matrix/matrix4-api','matrix/t','matrix/transform','matrix/transform-api'],function ( require ) { -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var constants = require( "constants" ); + var equal = require( "equal" ); -define('_math',['require','./lang','./constants','./vector/vector2','./vector/vector3','./vector/vector4','./vector/quaternion','./matrix/matrix2','./matrix/matrix3','./matrix/matrix4','./matrix/transform'],function ( require ) { + var V2 = require( "vector/v2" ); + var Vector2 = require( "vector/vector2" ); + var vector2 = require( "vector/vector2-api" ); - var lang = require( './lang' ), - constants = require( './constants' ), - vector2 = require( './vector/vector2' ), - vector3 = require( './vector/vector3' ), - vector4 = require( './vector/vector4' ), - quaternion = require( './vector/quaternion' ), - matrix2 = require( './matrix/matrix2' ), - matrix3 = require( './matrix/matrix3' ), - matrix4 = require( './matrix/matrix4' ), - transform = require( './matrix/transform' ); + var V3 = require( "vector/v3" ); + var Vector3 = require( "vector/vector3" ); + var vector3 = require( "vector/vector3-api" ); - var _Math = function( options ) { - - var _FLOAT_ARRAY_ENUM = { - Float32: Float32Array, - Float64: Float64Array - }; - - var _FLOAT_ARRAY_TYPE = _FLOAT_ARRAY_ENUM.Float32; - - Object.defineProperty( this, 'ARRAY_TYPE', { - get: function() { - return _FLOAT_ARRAY_TYPE; - }, - enumerable: true - }); - - lang.extend( this, constants() ); - - var _vector2 = vector2( _FLOAT_ARRAY_TYPE ); - var _vector3 = vector3( _FLOAT_ARRAY_TYPE ); - var _vector4 = vector4( _FLOAT_ARRAY_TYPE ); - var _quaternion = quaternion( _FLOAT_ARRAY_TYPE ); - - var _matrix2 = matrix2( _FLOAT_ARRAY_TYPE ); - var _matrix3 = matrix3( _FLOAT_ARRAY_TYPE ); - var _matrix4 = matrix4( _FLOAT_ARRAY_TYPE ); - var _transform = transform( _FLOAT_ARRAY_TYPE ); - - Object.defineProperty( this, 'Vector2', { - get: function() { - return _vector2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector2', { - get: function() { - return _vector2; - }, - enumerable: true - }); + var V4 = require( "vector/v4" ); + var Vector4 = require( "vector/vector4" ); + var vector4 = require( "vector/vector4-api" ); - Object.defineProperty( this, 'Vector3', { - get: function() { - return _vector3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector3', { - get: function() { - return _vector3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Vector4', { - get: function() { - return _vector4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector4', { - get: function() { - return _vector4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Quaternion', { - get: function() { - return _quaternion.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'quaternion', { - get: function() { - return _quaternion; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix2', { - get: function() { - return _matrix2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix2', { - get: function() { - return _matrix2; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix3', { - get: function() { - return _matrix3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix3', { - get: function() { - return _matrix3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix4', { - get: function() { - return _matrix4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix4', { - get: function() { - return _matrix4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Transform', { - get: function() { - return _transform.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'transform', { - get: function() { - return _transform; - }, - enumerable: true - }); - + var M2 = require( "matrix/m2" ); + var Matrix2 = require( "matrix/matrix2" ); + var matrix2 = require( "matrix/matrix2-api" ); + + var M3 = require( "matrix/m3" ); + var Matrix3 = require( "matrix/matrix3" ); + var matrix3 = require( "matrix/matrix3-api" ); + + var M4 = require( "matrix/m4" ); + var Matrix4 = require( "matrix/matrix4" ); + var matrix4 = require( "matrix/matrix4-api" ); + + var T = require( "matrix/t" ); + var Transform = require( "matrix/transform" ); + var transform = require( "matrix/transform-api" ); + + function extend( object, extra ) { + for ( var prop in extra ) { + if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { + object[prop] = extra[prop]; + } + } + } + + var _Math = function( options ) { + var FLOAT_ARRAY_ENUM = { + Float32: Float32Array, + Float64: Float64Array }; + this.FLOAT_ARRAY_ENUM = FLOAT_ARRAY_ENUM; + + var ARRAY_TYPE = this.ARRAY_TYPE = FLOAT_ARRAY_ENUM.Float32; + + extend( this, constants ); + this.equal = equal; + extend( this, { + V2: V2( ARRAY_TYPE ), + Vector2: Vector2( ARRAY_TYPE ), + vector2: vector2( ARRAY_TYPE ) + }); + extend( this, { + V3: V3( ARRAY_TYPE ), + Vector3: Vector3( ARRAY_TYPE ), + vector3: vector3( ARRAY_TYPE ) + }); + extend( this, { + V4: V4( ARRAY_TYPE ), + Vector4: Vector4( ARRAY_TYPE ), + vector4: vector4( ARRAY_TYPE ) + }); + extend( this, { + M2: M2( ARRAY_TYPE ), + Matrix2: Matrix2( ARRAY_TYPE ), + matrix2: matrix2( ARRAY_TYPE ) + }); + extend( this, { + M3: M3( ARRAY_TYPE ), + Matrix3: Matrix3( ARRAY_TYPE ), + matrix3: matrix3( ARRAY_TYPE ) + }); + extend( this, { + M4: M4( ARRAY_TYPE ), + Matrix4: Matrix4( ARRAY_TYPE ), + matrix4: matrix4( ARRAY_TYPE ) + }); + extend( this, { + T: T( ARRAY_TYPE ), + Transform: Transform( ARRAY_TYPE ), + transform: transform( ARRAY_TYPE ) + }); + }; - return new _Math(); + return new _Math(); }); - return require( "_math" ); + return require('_math'); })); if ( typeof define !== "function" ) { @@ -2792,7 +12154,9 @@ define('core/dependency-scheduler',['require','common/graph'],function ( require return undefined; } var taskId = this._schedule.shift(); - return this._tasks[taskId]; + var task = this._tasks[taskId]; + this.remove( taskId ); + return task; } function hasNext() { @@ -3712,6 +13076,7 @@ define('core/function-task',['require','common/guid','when'],function ( require if( task._taskState === T_CANCELLED ) { task._runState = R_RESOLVED; task._taskState = T_CLOSED; + task._scheduler.remove( task.id ); } else if( task._taskState === T_STARTED ) { // Run the task result = task._thunk.call( this._context, result ); @@ -3725,22 +13090,22 @@ define('core/function-task',['require','common/guid','when'],function ( require task._deferred.resolve( task.result ); } else { task.result = when( result, - // callback - function( value ) { - task.result = value; - task._runState = R_RESOLVED; - if( task._taskState === T_STARTED ) { - task._scheduler.insert( task, task.id, task._schedule ); - } - }, - // errback - function( error ) { - task.result = error; - task._runState = R_REJECTED; - if( task._threadState === T_STARTED ) { - task._scheduler.insert( task, task.id, task._schedule ); + // callback + function( value ) { + task.result = value; + task._runState = R_RESOLVED; + if( task._taskState === T_STARTED ) { + task._scheduler.insert( task, task.id, task._schedule ); + } + }, + // errback + function( error ) { + task.result = error; + task._runState = R_REJECTED; + if( task._taskState === T_STARTED ) { + task._scheduler.insert( task, task.id, task._schedule ); + } } - } ); } } else { @@ -3748,8 +13113,10 @@ define('core/function-task',['require','common/guid','when'],function ( require } } catch( exception ) { task.result = exception; + task._taskState = T_CLOSED; task._runState = R_REJECTED; task._deferred.reject( exception ); + console.log( "Task", task.id, ": ", exception.stack ); } task._scheduler.current = null; @@ -4727,70 +14094,176 @@ define('core/components/transform',['require','_math','common/extend','base/comp var Transform = function( position, rotation, scale ) { Component.call( this, "Transform", null, [] ); - this.position = position ? new math.Vector3( position ) : math.vector3.zero; - this.rotation = rotation ? new math.Vector3( rotation ) : math.vector3.zero; - this.scale = scale ? new math.Vector3( scale ) : math.vector3.one; - this._cachedMatrix = math.matrix4.identity; - this._cachedIsValid = false; - this._cachedAbsolute = math.matrix4.identity; + // Local position + this._position = position ? new math.Vector3( position ) : new math.Vector3( math.vector3.zero ); + this.__defineGetter__( "position", function() { + return this._position; + }); + this.__defineSetter__( "position", function( value ) { + this._position.set( value ); + this._cachedLocalMatrixIsValid = false; + this._cachedWorldMatrixIsValid = false; + }); + + // Local rotation + this._rotation = rotation ? new math.Vector3( rotation ) : new math.Vector3( math.vector3.zero ); + this.__defineGetter__( "rotation", function() { + return this._rotation; + }); + this.__defineSetter__( "rotation", function( value ) { + this._rotation.set( value ); + this._cachedLocalMatrixIsValid = false; + this._cachedWorldMatrixIsValid = false; + }); + this._rotationMatrix = new math.transform.rotate( this._rotation ); + this._rotationMatrixIsValid = true; + + // Local scale + this._scale = scale ? new math.Vector3( scale ) : new math.Vector3( math.vector3.one ); + this.__defineGetter__( "scale", function() { + return this._scale; + }); + this.__defineSetter__( "scale", function( value ) { + this._scale.set( value ); + this._cachedLocalMatrixIsValid = false; + this._cachedWorldMatrixIsValid = false; + }); + + this._cachedLocalMatrix = new math.T(); + this._cachedLocalMatrixIsValid = false; + this._cachedWorldMatrix = new math.T(); + //TODO: Make the world matrix caching actually do something + this._cachedWorldMatrixIsValid = false; + this._tempMatrix = new math.T(); }; Transform.prototype = new Component(); Transform.prototype.constructor = Transform; - function matrix() { - if( this._cachedIsValid ) { - return this._cachedMatrix; + // Return the relative transform + function computeLocalMatrix() { + if( this._cachedLocalMatrixIsValid && !this.position.modified && !this.rotation.modified && !this.scale.modified) { + return this._cachedLocalMatrix; } else { - // debugger; - this._cachedMatrix = math.transform.fixed( this.position, this.rotation, - this.scale ); - this._cachedIsValid = true; - return this._cachedMatrix; + math.transform.set(this._cachedLocalMatrix, this.position.buffer, this.rotation.buffer, this.scale.buffer); + this._cachedLocalMatrixIsValid = true; + this.position.modified = false; + this.rotation.modified = false; + this.scale.modified = false; + return this._cachedLocalMatrix; } } - function setPosition( position ) { - math.vector3.set( this.position, position[0], position[1], position[2] ); - this._cachedIsValid = false; - - return this; + // Return the world transform + function computeWorldMatrix() { + if( this.owner && this.owner.parent && + this.owner.parent.hasComponent( "Transform" ) ) { + var parentTransform = this.owner.parent.findComponent( "Transform" ); + math.matrix4.multiply( parentTransform.worldMatrix(), computeLocalMatrix.call( this), + this._cachedWorldMatrix ); + } else { + math.matrix4.set( this._cachedWorldMatrix, computeLocalMatrix.call( this) ); + } + return this._cachedWorldMatrix; } - function setRotation( rotation ) { - math.vector3.set( this.rotation, rotation[0], rotation[1], rotation[2] ); - this._cachedIsValid = false; - - return this; + //This calculates the rotation of the object relative to world space + function computeWorldRotation(){ + //TODO: Add caching of results in here once we have a way of detecting changes in the parents + if( this.owner && this.owner.parent && + this.owner.parent.hasComponent( "Transform" ) ) { + return math.matrix4.multiply(this.owner.parent.findComponent( "Transform").worldRotation(), + math.transform.rotate(this._rotation.buffer)); + }else{ + return math.transform.rotate(this._rotation.buffer); + } } - function setScale( scale ) { - math.vector3.set( this.scale, scale[0], scale[1], scale[2] ); - this._cachedIsValid = false; + //This calculates the rotation of world space relative to the object + //It is wrong! + function computeLocalRotation(){ + //TODO: Add caching of results in here once we have a way of detecting changes in the parents + if( this.owner && this.owner.parent && + this.owner.parent.hasComponent( "Transform" ) ) { + return math.matrix4.multiply(math.transform.rotate(this._rotation.buffer), + this.owner.parent.findComponent( "Transform").localRotation()); + }else{ + return math.transform.rotate(this._rotation.buffer); + } + } - return this; + function directionToWorld(direction, result) { + result = result || new math.V3(); + math.matrix4.multiply( + computeWorldRotation.call(this), + math.transform.translate( direction ), + this._tempMatrix); + math.vector3.set(result, this._tempMatrix[3], this._tempMatrix[7], this._tempMatrix[11]); + return result; } - function absolute() { - if( this.owner && this.owner.parent && - this.owner.parent.hasComponent( "Transform" ) ) { - var parentTransform = this.owner.parent.findComponent( this.type ); - this._cachedAbsolute = math.matrix4.multiply( [matrix.call( this ), parentTransform.absolute()] ); - } else { - this._cachedAbsolute = matrix.call( this ); + function directionToLocal(direction, result) { + result = result || new math.V3(); + if( this.owner && this.owner.parent && + this.owner.parent.hasComponent( "Transform" ) ) { + var thisParentWorldMatrix = this.owner.parent.findComponent( "Transform").worldMatrix(); + //Multiply the inverse of the parent's world matrix by the other transform's world matrix, + // putting the result in thisWorldPositionMatrix + //Solution grabbed from http://www.macaronikazoo.com/?p=419 + math.matrix4.multiply(math.matrix4.inverse(thisParentWorldMatrix,this._tempMatrix), math.matrix4.translate(direction), this._tempMatrix); + //Subtract this turret's position so that everything is offset properly + math.vector3.set(result, this._tempMatrix[3] - this._position.buffer[0], this._tempMatrix[7] - this._position.buffer[1], this._tempMatrix[11] - this._position.buffer[2]); + } + else{ + math.vector3.set(result, direction[0], direction[1], direction[2]); } - return this._cachedAbsolute; + return result; } - function relative() { - throw new Error( "not implemented" ); + function toWorldPoint() { + var worldMatrix = computeWorldMatrix.call(this); + return [worldMatrix[3], worldMatrix[7], worldMatrix[11]]; + } + + function transformToLocal(otherTransform, result) + { + result = result || new math.V3(); + var otherWorldMatrix = otherTransform.worldMatrix(); + if( this.owner && this.owner.parent && + this.owner.parent.hasComponent( "Transform" ) ) { + var thisParentWorldMatrix = this.owner.parent.findComponent( "Transform").worldMatrix(); + //Multiply the inverse of the parent's world matrix by the other transform's world matrix, + // putting the result in thisWorldPositionMatrix + // Solution grabbed from http://www.macaronikazoo.com/?p=419 + math.matrix4.multiply(math.matrix4.inverse(thisParentWorldMatrix,this._tempMatrix), otherWorldMatrix, this._tempMatrix); + //Subtract this turret's position so that everything is offset properly + math.vector3.set(result, this._tempMatrix[3] - this._position.buffer[0], this._tempMatrix[7] - this._position.buffer[1], this._tempMatrix[11] - this._position.buffer[2]); + } + else{ + math.vector3.set(result, otherWorldMatrix[3], otherWorldMatrix[7], otherWorldMatrix[11]); + } + return result; } var prototype = { - setPosition: setPosition, - setRotation: setRotation, - setScale: setScale, - absolute: absolute, - relative: relative + //TODO: worldMatrix and localMatrix look like property accessors from the outside but are actually methods. This should be changed, either so that they are accessed like properties or look like methods + worldMatrix: computeWorldMatrix, + localMatrix: computeLocalMatrix, + directionToLocal: directionToLocal, + directionToWorld: directionToWorld, + //Same thing goes for this one. + worldRotation: computeWorldRotation, + localRotation: computeLocalRotation, + toWorldPoint: toWorldPoint, + transformToLocal: transformToLocal, + lookAt: undefined, + target: undefined, + // Direction constants + forward: new math.Vector3( 0, 0, 1 ), + backward: new math.Vector3( 0, 0, -1 ), + left: new math.Vector3( -1, 0, 0 ), + right: new math.Vector3( 1, 0, 0 ), + up: new math.Vector3( 0, 1, 0 ), + down: new math.Vector3( 0, -1, 0 ) }; extend( Transform.prototype, prototype ); diff --git a/gladius/gladius-cubicvr.js b/gladius/gladius-cubicvr.js index 2b1073c..2dff40b 100644 --- a/gladius/gladius-cubicvr.js +++ b/gladius/gladius-cubicvr.js @@ -21369,227 +21369,39 @@ if ( typeof define !== "function" ) { define('core/event',['require'],function( require ) { - var Event = function( type, data, queue ) { - function dispatcher() { - var i, l; - for( i = 0, l = arguments.length; i < l; ++ i ) { - try { - var handler = arguments[i]; - if( handler.handleEvent ) { - handler.handleEvent( dispatcher ); - } - } catch( error ) { - console.log( error ); - } - } - } - - dispatcher.type = type; - dispatcher.data = data; - dispatcher.queue = undefined !== queue ? queue : true; - - return dispatcher; - - }; - - return Event; - -}); -if ( typeof define !== "function" ) { - var define = require( "amdefine" )( module ); -} - -define('src/services/renderer',['require','base/service','CubicVR','src/services/target','core/event'],function ( require ) { - - var Service = require( "base/service" ); - require( "CubicVR" ); - var Target = require( "src/services/target" ); - var Event = require( "core/event" ); - - var Renderer = function( scheduler, options ) { - options = options || {}; - - var schedules = { - "render": { - tags: ["@render", "graphics"], - dependsOn: [] - } - }; - Service.call( this, scheduler, schedules ); - - this.target = new Target( options.canvas ); - }; - - function render() { - var context = this.target.context; - var registeredComponents = this._registeredComponents; - var gl = context.GLCore.gl; - var spaces = {}; - var sIndex, sLength; - var component; - - // Update all graphics components - var updateEvent = new Event( 'Update', undefined, false ); - for( var componentType in registeredComponents ) { - for( var entityId in registeredComponents[componentType] ) { - component = registeredComponents[componentType][entityId]; - while( component.handleQueuedEvent() ) {} - updateEvent( component ); - } - } - - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - - var cameraOwnerIds = Object.keys( registeredComponents["Camera"] || {} ); - cameraOwnerIds.forEach( function( id ) { - var ownerSpace = registeredComponents["Camera"][id].owner.space; - if( !spaces.hasOwnProperty( ownerSpace.id ) ) { - spaces[ownerSpace.id] = ownerSpace; - } - }); - var spaceIds = Object.keys( spaces ); - - for( sIndex = 0, sLength = spaceIds.length; sIndex < sLength; ++ sIndex ) { - var spaceId = spaceIds[sIndex]; - var space = spaces[spaceId]; - var i, l; - var cameraEntities = space.findAllWith( "Camera" ); - var modelEntities = space.findAllWith( "Model" ); - var lightEntities = space.findAllWith( "Light" ); - - // Handle lights for the current space - var cubicvrLights = []; - for( i = 0, l = lightEntities.length; i < l; ++ i ) { - var light = lightEntities[i].findComponent( "Light" ); - cubicvrLights.push( light._cubicvrLight ); - } - - // Render the space for each camera - for( i = 0, l = cameraEntities.length; i < l; ++ i ) { - var camera = cameraEntities[ i ].findComponent( "Camera" ); - - cubicvrLights.forEach( function( light ) { - light.prepare( camera._cubicvrCamera ); - }); - - for( var mi = 0, ml = modelEntities.length; mi < ml; ++mi ) { - var model = modelEntities[ mi ].findComponent( "Model" ); - var transform = modelEntities[ mi ].findComponent( "Transform" ); - - model._cubicvrMesh.instanceMaterials = [model._cubicvrMaterialDefinition]; - - context.renderObject( - model._cubicvrMesh, - camera._cubicvrCamera, - transform.absolute(), - cubicvrLights - ); + function dispatch() { + var dispatchList = Array.prototype.slice.call( arguments, 0 ); + var i, l; - model._cubicvrMesh.instanceMaterials = null; + if( dispatchList.length > 0 && Array.isArray( dispatchList[0] ) ) { + dispatchList = dispatchList[0]; + } + for( i = 0, l = dispatchList.length; i < l; ++ i ) { + try { + var handler = dispatchList[i]; + if( handler.handleEvent ) { + handler.handleEvent.call( handler, this ); } + } catch( error ) { + console.log( error ); } } } - Renderer.prototype = new Service(); - Renderer.prototype.constructor = Renderer; - Renderer.prototype.render = render; - - return Renderer; - -}); -if ( typeof define !== "function" ) { - var define = require( "amdefine" )( module ); -} - -define('common/extend',['require'],function ( require ) { - - - function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } - } - return object; - } - - return extend; - -}); - -if ( typeof define !== "function" ) { - var define = require( "amdefine" )( module ); -} - -define('base/component',['require','core/event'],function( require ) { - - var Event = require( "core/event" ); - - var Component = function( type, provider, dependsOn ) { - this.type = type; // String identifying the type of this component - this.provider = provider; // Reference to the object instance that provides - // this component - this.dependsOn = dependsOn || []; // List of component types that this - // component depends on - this.owner = null; // Reference to the entity instance that owns this - this._queuedEvents = []; // List of queued events - }; - - function setOwner( owner ) { - if( owner !== this.owner ) { - var previous = this.owner; - this.owner = owner; - var event = new Event( - 'ComponentOwnerChanged', - { - current: owner, - previous: previous - }, - false - ); - event( this ); - } - } - - function handleEvent( event ) { - if( "on" + event.type in this ) { - if( event.queue ) { - this._queuedEvents.push( event ); - } else { - var handler = this["on" + event.type]; - try { - handler.call( this, event ); - } catch( error ) { - console.log( error ); - } - } + var Event = function( type, data, queue ) { + if( undefined === type || type.length < 1 ) { + throw new Error( "event must have a non-trivial type" ); } - } - - function handleQueuedEvent() { - if( this._queuedEvents.length > 0 ) { - var event = this._queuedEvents.shift(); - if( "on" + event.type in this ) { - var handler = this["on" + event.type]; - try { - handler.call( this, event ); - } catch( error ) { - debugger; - console.log( error ); - } - } + this.type = type; + this.data = data; + if( undefined === queue ) { + queue = true; } - return this._queuedEvents.length; - } - - Component.prototype = { - setOwner: setOwner, - handleEvent: handleEvent, - handleQueuedEvent: handleQueuedEvent + this.queue = queue; + this.dispatch = dispatch.bind( this ); }; - return Component; + return Event; }); /** @@ -21902,1689 +21714,11098 @@ var requirejs, require, define; define("../tools/almond", function(){}); -/*jshint white: false, strict: false, plusplus: false */ -/*global define: false */ +define('constants',['require'],function ( require ) { -//JS language helpers. + return { + TAU: 2 * Math.PI, + PI: Math.PI + }; -//Array Remove - By John Resig (MIT Licensed) -//Done outside the define call since it should be immediately -//before dependency tracing is done for any module. -if ( !Array.prototype.remove ) { - Array.prototype.remove = function(from, to) { - var rest = this.slice( (to || from) + 1 || this.length ); - this.length = from < 0 ? this.length + from : from; - return this.push.apply(this, rest); - }; -} +}); +define('equal',['require'],function ( require ) { -define('lang',['require'],function ( require ) { + function equal( arg1, arg2, e ) { + e = e || 0.000001; - return { - // Simple bind function to maintain "this" for a function. - bind: function bind( obj, func ) { - return function() { - return func.apply( obj, arguments ); - }; - }, + return Math.abs( arg1 - arg2 ) < e; + } + + return equal; - extend: function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } //if - } //for - } //extend - }; }); +define('vector/v',['require'],function ( require ) { -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var V = function() { + }; -define('constants',['require'],function ( require ) { + return V; - return function() { - - var constants = { - - TAU: 2 * Math.PI, - - PI: Math.PI, - - HALF_PI: Math.PI / 2.0 - - }; - - return constants; - - }; - }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ - -define('vector/vector',['require'],function ( require ) { +define('vector/v2',['require','vector/v'],function ( require ) { - return function( FLOAT_ARRAY_TYPE ) { + var V = require( "vector/v" ); - var Vector = function( dim, args ) { + return function( FLOAT_ARRAY_TYPE ) { - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } + var V2 = function() { + var argc = arguments.length; + var i, j, vi = 0; - var vector = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - vector[i] = elements[i]; - } + var vector = new FLOAT_ARRAY_TYPE( 2 ); - return vector; + for( i = 0; i < argc && vi < 2; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 2; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 2; ++ vi ) { + vector[vi] = 0; + } - }; + return vector; + }; + V2.prototype = new V(); + V2.prototype.constructor = V2; - var vector = { - - $: Vector, + return V2; - add: function( v1, v2, result ) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] += v1[i] + v2[i]; - } + }; - return result; - }, +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + - clear: function( v ) { - for( var i = 0; i < v.length; ++ i ) { - v[i] = 0; - } - }, + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; - dot: function( v1, v2 ) { - var res = 0; - for( var i = 0; i < v1.length; ++ i) { - res += v1[i] * v2[i]; - } - return res; - }, + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; - equal: function( v1, v2, e ) { - e = e || 0.000001; + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; - if( v1.length != v2.length ) { - return false; - } + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } - var dim = v1.length; - for( var i = 0; i < dim; ++ i ) { - if ( Math.abs(v1[i] - v2[i]) > e ) { - return false; - } - } + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } - return true; - }, + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { - length: function( v ) { - var va = 0; - for( var i = 0; i < v.length; ++ i ) { - va += v[i] * v[i]; - } + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, - return Math.sqrt(va); - }, + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, - multiply: function( v, s, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * s; - } + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, - return result; - }, - - negate: function( v, result ) { - for( var i = 0; i < v.length; ++ i ) { - result[i] = v[i] * -1; - } - - return result; - }, + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; - normalize: function( v, result ) { - var len = v.length; - for( var i = 0, abslen = vector.length(v); i < len; ++ i ) { - result[i] = v[i] / abslen; - } + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; - return result; - }, + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; - subtract: function( v1, v2, result) { - for( var i = 0; i < v1.length; ++ i ) { - result[i] = v1[i] - v2[i]; - } + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; - return result; - } + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; - }; - - Object.defineProperty( vector, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; - Object.defineProperty( vector, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; - Object.defineProperty( vector, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; - Object.defineProperty( vector, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; - Object.defineProperty( vector, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } - Object.defineProperty( vector, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } - return vector; + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } - }; + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } -define('vector/vector2',['require','./vector','../constants'],function ( require ) { + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } - return function( FLOAT_ARRAY_TYPE ) { + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); - var constants = require( '../constants' )(); + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); - var Vector2 = function() { - if( 0 === arguments.length ) { - return vector.$( 2, [0, 0] ); - } else { - return vector.$( 2, arguments ); - } - }; - - var vector2 = { - - $: Vector2, - - add: function( v1, v2, result ) { - result = result || Vector2(); + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } - return vector.add( v1, v2, result ); - }, + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } - angle: function( v1, v2 ) { - var nV1 = Vector2(); - var nV2 = Vector2(); + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } - vector.normalize(v1, nV1); - vector.normalize(v2, nV2); + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); - return Math.acos( vector.dot( nV1, nV2 ) ); - }, + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); - clear: vector.clear, + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); - dot: vector.dot, + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); - equal: vector.equal, + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); - length: vector.length, + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); - multiply: function( v, s, result ) { - result = result || Vector2(); + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } - return vector.multiply( v, s, result ); - }, - - negate: function( v, result ) { - result = result || Vector2(); - - return vector.negate( v, result ); - }, + var length = collection.length, + noaccum = arguments.length < 3; - normalize: function( v, result ) { - result = result || Vector2(); - var len = vector.length(v); + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; - result[0] = v[0]/len; - result[1] = v[1]/len; + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } - return result; - }, - - project: function( v1, v2, result ) { - result = result || Vector2(); - - var dp = v1[0]*v2[0] + v1[1]*v2[1]; - var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); + var prop, + props = keys(collection); - result[0] = dp_over_v2_squared_length * v2[0]; - result[1] = dp_over_v2_squared_length * v2[1]; - - return result; - }, - - set: function( v, x, y ) { - v[0] = x; - v[1] = y; - }, - - subtract: function( v1, v2, result ) { - result = result || Vector2(); + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } - return vector.subtract( v1, v2, result ); - } - - }; - - Object.defineProperty( vector2, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); - Object.defineProperty( vector2, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); - Object.defineProperty( vector2, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); - Object.defineProperty( vector2, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); - Object.defineProperty( vector2, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } - Object.defineProperty( vector2, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; - return vector2; + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } - }; + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } -define('vector/vector3',['require','./vector'],function ( require ) { + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } - return function( FLOAT_ARRAY_TYPE ) { + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; - var Vector3 = function() { - if( 0 === arguments.length ) { - return vector.$( 3, [0, 0, 0] ); - } else { - return vector.$( 3, arguments ); - } - }; + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } - var vector3 = { - - $: Vector3, + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } - add: function( v1, v2, result ) { - result = result || Vector3(); + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); - return vector.add( v1, v2, result ); - }, + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } - angle: function( v1, v2 ) { + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2])) - ); - }, + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } - clear: vector.clear, + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; - cross: function( v1, v2, result ) { - result = result || Vector3(); + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } - result[0] = (v1[1] * v2[2]) - (v2[1] * v1[2]); - result[1] = (v1[2] * v2[0]) - (v2[2] * v1[0]); - result[2] = (v1[0] * v2[1]) - (v2[0] * v1[1]); + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; - return result; - }, + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - }, + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } - equal: vector.equal, + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } - length: vector.length, + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } - multiply: function( v, s, result ) { - result = result || Vector3(); + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; - return vector.multiply( v, s, result ); - }, + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } - normal: function( v1, v2, result ) { - result = result || Vector3(); + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } - return Vector3.cross( v1, v2, result ); - }, + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } - normalize: function( v, result ) { - result = result || Vector3(); - var len = vector.length(v); + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } - return result; - }, - - set: function( v, x, y, z ) { - v[0] = x; - v[1] = y; - v[2] = z; - }, + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); - subtract: function( v1, v2, result ) { - result = result || Vector3(); + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } - return vector.subtract( v1, v2, result ); - } + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; - }; - - Object.defineProperty( vector3, 'x', { - get: function() { - return Vector3( [1, 0, 0] ); - }, - enumerable: true - }); + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } - Object.defineProperty( vector3, 'y', { - get: function() { - return Vector3( [0, 1, 0] ); - }, - enumerable: true - }); + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } - Object.defineProperty( vector3, 'z', { - get: function() { - return Vector3( [0, 0, 1] ); - }, - enumerable: true - }); + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } - Object.defineProperty( vector3, 'zero', { - get: function() { - return Vector3( [0, 0, 0] ); - }, - enumerable: true - }); + var partialArgs = slice.call(arguments, 2); - Object.defineProperty( vector3, 'one', { - get: function() { - return Vector3( [1, 1, 1] ); - }, - enumerable: true - }); + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; - return vector3; + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; - }; + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } -define('vector/vector4',['require','./vector'],function ( require ) { + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; - return function( FLOAT_ARRAY_TYPE ) { + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; + } - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } - var Vector4 = function() { - if( 0 === arguments.length ) { - return vector.$( 4, [0, 0, 0, 0] ); - } else { - return vector.$( 4, arguments ); - } - }; + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; - var vector4 = { - - $: Vector4, + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); - add: function( v1, v2, result ) { - result = result || Vector4(); + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } - result[0] = v1[0] + v2[0]; - result[1] = v1[1] + v2[1]; - result[2] = v1[2] + v2[2]; - result[3] = v1[3] + v2[3]; + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } - return result; - }, + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } - // Computes the angle between v1 and v2 - angle: function( v1, v2 ) { - return Math.acos( - (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]) / - (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * - Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3])) - ); - }, + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } - clear: vector.clear, + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; - // Computes the dot product of v1 and v2 - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; - }, + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } - equal: vector.equal, + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; - length: vector.length, + return function() { + var result, + others = arguments; - // Computes v * s - multiply: function( v, s, result ) { - result = result || Vector4(); + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } - return vector.multiply( v, s, result ); - }, + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } - // Computes a Vector4 with same direction as v having unit length - normalize: function( v, result ) { - result = result || Vector4(); - var len = vector.length(v); + return function() { + var now = new Date, + remain = wait - (now - lastCalled); - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; - result[3] = v[3]/len; + args = arguments; + thisArg = this; - return result; - }, - - set: function( v, x, y, z, w ) { - v[0] = x; - v[1] = y; - v[2] = z; - v[3] = w; - }, - - // Computes v1 - v2 - subtract: function( v1, v2, result ) { - result = result || Vector4(); - - return vector.subtract( v1, v2, result ); - } + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } - } - - Object.defineProperty( vector4, 'x', { - get: function() { - return Vector4( [1, 0, 0, 0] ); - }, - enumerable: true - }); + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { + return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } - Object.defineProperty( vector4, 'y', { - get: function() { - return Vector4( [0, 1, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'z', { - get: function() { - return Vector4( [0, 0, 1, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( vector4, 'w', { - get: function() { - return Vector4( [0, 0, 0, 1] ); - }, - enumerable: true - }); + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } - Object.defineProperty( vector4, 'zero', { - get: function() { - return Vector4( [0, 0, 0, 0] ); - }, - enumerable: true - }); + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); - Object.defineProperty( vector4, 'one', { - get: function() { - return Vector4( [1, 1, 1, 1] ); - }, - enumerable: true - }); + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); - return vector4; + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); }; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; -define('vector/quaternion',['require','./vector4','./vector3'],function ( require ) { + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } - return function( FLOAT_ARRAY_TYPE ) { + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } - var vector4 = require( './vector4' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( './vector3' )( FLOAT_ARRAY_TYPE ); + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } - var Quaternion = vector4.$; + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); - var quaternion = { + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } - $: Quaternion, + var index = -1, + result = true, + size = 0; - to: { - rpy: function( q, result ) { - var r = result || vector3.$(); - var atan2 = Math.atan2, - asin = Math.asin; + // add the first collection to the stack of traversed objects + stack.push(a); - r[0] = atan2( 2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] + 2*q[2]*q[2] ); - r[1] = asin( 2*q[0]*q[2] - 2*q[3]*q[1] ); - r[2] = atan2( 2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] + 2*q[3]*q[3] ); + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; - if( !result ) { - return r; - } - } - }, - - from: { - rpy: function( v, result ) { - var r = result || quaternion.$(); - var sin = Math.sin, - cos = Math.cos; - var half_phi = v[0] / 2, - half_theta = v[1] / 2, - half_psi = v[2] / 2; - var sin_half_phi = sin( half_phi ), - cos_half_phi = cos( half_phi ), - sin_half_theta = sin( half_theta ), - cos_half_theta = cos( half_theta ), - sin_half_psi = sin( half_psi ), - cos_half_psi = cos( half_psi ); - - r[0] = cos_half_phi * cos_half_theta * cos_half_psi + - sin_half_phi * sin_half_theta * sin_half_psi; - r[1] = sin_half_phi * cos_half_theta * cos_half_psi - - cos_half_phi * sin_half_theta * sin_half_psi; - r[2] = cos_half_phi * sin_half_theta * cos_half_psi + - sin_half_phi * cos_half_theta * sin_half_psi; - r[3] = cos_half_phi * cos_half_theta * sin_half_psi - - sin_half_phi * sin_half_theta * cos_half_psi; - - if( !result ) { - return r; - } - } - }, + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } - length: vector4.length, + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } - multiply: function( q1, q2, result ) { - var r = result || quaternion.$(); + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } - r[0] = q1[3] * q2[0] + q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1]; // x - r[1] = q1[3] * q2[1] - q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0]; // y - r[2] = q1[3] * q2[2] + q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3]; // z - r[3] = q1[3] * q2[3] - q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2]; // w + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } - if( !result ) { - return r; - } - }, + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } - normalize: vector4.normalize + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } - }; + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } - Object.defineProperty( quaternion, 'identity', { - get: function() { - return Quaternion( [0, 0, 0, 1] ); - }, - enumerable: true - }); + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } - return quaternion; + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } - }; + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } -}); + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } -define('matrix/matrix',['require'],function ( require ) { + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } - return function( FLOAT_ARRAY_TYPE ) { - - var Matrix = function( dim, args ) { - - var elements = null; - if( 1 === args.length ) { - elements = args[0]; - } else { - elements = args; - } - - var matrix = new FLOAT_ARRAY_TYPE( dim ); - for( var i = 0; i < dim; ++ i ) { - matrix[i] = elements[i]; - } - - return matrix; - - }; - - var matrix = { - - $: Matrix, - - add: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - result[i] += m1[i] + m2[i]; - } + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); - return result; - }, - - subtract: function( m1, m2, result ) { - for( var i = 0; i < m1.length; ++ i ) { - m1[i] -= m2[i]; - } - return m1; - }, - - clear: function( m ) { - for( var i = 0; i < m.length; ++ i ) { - m[i] = 0; - } - }, - - equal: function( m1, m2, e ) { - e = e || 0.000001; + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } - if( m1.length != m2.length ) { - return false; - } - - var dim = m1.length; - for( var i = 0; i < dim; ++ i ) { - if( Math.abs( m1[i] - m2[i] ) > e ) { - return false; - } - } + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } - return true; - } - - }; - - return matrix; - - }; - -}); + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } -define('matrix/matrix2',['require','./matrix'],function ( require ) { + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } - return function( FLOAT_ARRAY_TYPE ) { + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; - var Matrix2 = function() { - if( 0 === arguments.length ) { - return matrix.$( 4, [0, 0, - 0, 0] ); - } else { - return matrix.$( 4, arguments ); - } - }; + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; - var matrix2 = { + // clear stored code snippets + tokenized.length = 0; - $: Matrix2, + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; - add: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } - subtract: function( ml, result ) { - result = result || Matrix2(); - var temp = ml[0]; - - if (ml.length == 1) { - result = temp; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } - clear: matrix.clear, + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } - equal: matrix.equal, + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } - determinant: function( m ) { - return m[0]*m[3] - m[1]*m[2]; - }, - - inverse: function( m, result ) { - - var det = matrix2.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix2(); - - result[0] = m[3]/det; - result[1] = m[1]*-1/det; - result[2] = m[2]*-1/det; - result[3] = m[0]/det; - - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix2(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][2]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][3]; - result[2] = temp[2]*ml[i][0] + temp[3]*ml[i][2]; - result[3] = temp[2]*ml[i][1] + temp[3]*ml[i][3]; - temp = result; - } - } - return result; - }, - - transpose: function( m, result ) { - result = result || Matrix2(); - - var temp = m[1]; - result[0] = m[0]; - result[1] = m[2]; - result[2] = temp; - result[3] = m[3]; - - return result; - } - } + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } - Object.defineProperty( matrix2, 'zero', { - get: function() { - return Matrix2( [0, 0, - 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'one', { - get: function() { - return Matrix2( [1, 1, - 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix2, 'identity', { - get: function() { - return Matrix2( [1, 0, - 0, 1] ); - }, - enumerable: true - }); - - return matrix2; - - }; + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } -define('matrix/matrix3',['require','./matrix'],function ( require ) { + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } - return function( FLOAT_ARRAY_TYPE ) { + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } - var Matrix3 = function() { - if( 0 === arguments.length ) { - return matrix.$( 9, [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - } else { - return matrix.$( 9, arguments ); - } - }; + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; + }; + }); - var matrix3 = { - - $: Matrix3, + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; - add: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); - subtract: function( ml, result ) { - result = result || Matrix3(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); - clear: matrix.clear, + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('vector/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); - equal: matrix.equal, +define('common/not-implemented',['require'],function ( require ) { - determinant: function( m ) { + return function notImplemented() { + throw new Error( "not implemented" ); + }; - return m[0]*(m[4]*m[8] - m[5]*m[7]) - - m[1]*(m[3]*m[8] - m[5]*m[6]) - + m[2]*(m[3]*m[7] - m[4]*m[6]); - }, - - inverse: function( m, result ) { - var det = matrix3.determinant(m); - if (det == 0) - throw 'matrix is singular'; - - result = result || Matrix3(); - - result[0] = (m[8]*m[4] - m[7]*m[5])/det; - result[1] = -(m[8]*m[1] - m[7]*[2])/det; - result[2] = (m[5]*m[1] - m[4]*m[2])/det; - - result[3] = -(m[8]*m[3] - m[6]*m[5])/det; - result[4] = (m[8]*m[0] - m[6]*m[2])/det; - result[5] = -(m[5]*m[0] - m[3]*m[2])/det; - - result[6] = (m[7]*m[3] - m[6]*m[4])/det; - result[7] = -(m[7]*m[0] - m[6]*m[1])/det; - result[8] = (m[4]*m[0] - m[3]*m[1])/det; +}); +define('vector/vector',['require'],function ( require ) { - return result; - }, - - multiply: function( ml, result ) { - result = result || Matrix3(); - - if (ml.length == 1) - return ml[0]; - else { + var Vector = function() { + }; - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { + return Vector; - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][3] + temp[2]*ml[i][6]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][4] + temp[2]*ml[i][7]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][5] + temp[2]*ml[i][8]; +}); +define('vector/vector2-api',['require','common/not-implemented','vector/v2'],function ( require ) { - result[3] = temp[3]*ml[i][0] + temp[4]*ml[i][3] + temp[5]*ml[i][6]; - result[4] = temp[3]*ml[i][1] + temp[4]*ml[i][4] + temp[5]*ml[i][7]; - result[5] = temp[3]*ml[i][2] + temp[4]*ml[i][5] + temp[5]*ml[i][8]; + return function( FLOAT_ARRAY_TYPE ) { - result[6] = temp[6]*ml[i][0] + temp[7]*ml[i][3] + temp[8]*ml[i][6]; - result[7] = temp[6]*ml[i][1] + temp[7]*ml[i][4] + temp[8]*ml[i][7]; - result[8] = temp[6]*ml[i][2] + temp[7]*ml[i][5] + temp[8]*ml[i][8]; - - temp = result; - } - } - return result; - }, - - // Convert a vector rotation (in radians) to a 3x3 matrix - rotate: function( v, result ) { - var r = result || matrix3.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix3.$([ cosA, sinA, 0, - -sinA, cosA, 0, - 0, 0, 1 ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix3.$([ cosA, 0, -sinA, - 0, 1, 0, - sinA, 0, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + function add( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + + return result; + } + + function angle( v1, v2 ) { + var normalizedV1 = new V2(); + var normalizedV2 = new V2(); - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix3.$([ 1, 0, 0, - 0, cosA, sinA, - 0, -sinA, cosA ] )); - ml.push(matrix3.$(r)); - - matrix3.multiply( ml, r ); - } + normalize(v1, normalizedV1); + normalize(v2, normalizedV2); - if( !result ) { - return r; - } - }, + return Math.acos( dot( normalizedV1, normalizedV2 ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + + return v; + } + + function dot( v1, v2 ) { + var r = 0; + + r += v1[0] * v2[0]; + r += v1[1] * v2[1]; + + return r; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; - transpose: function( m, result ) { - result = result || Matrix3(); + if( v1.length !== v2.length ) { + return false; + } - var a01 = m[1], a02 = m[2], a12 = m[5]; - - result[0] = m[0]; - result[1] = m[3]; - result[2] = m[6]; - result[3] = a01; - result[4] = m[4]; - result[5] = m[7]; - result[6] = a02; - result[7] = a12; - result[8] = m[8]; + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); - return result; - } + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e ) { + return false; + } - }; - - Object.defineProperty( matrix3, 'zero', { - get: function() { - return Matrix3( [0, 0, 0, - 0, 0, 0, - 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix3, 'one', { - get: function() { - return Matrix3( [1, 1, 1, - 1, 1, 1, - 1, 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix3, 'identity', { - get: function() { - return Matrix3( [1, 0, 0, - 0, 1, 0, - 0, 0, 1] ); - }, - enumerable: true - }); + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V2(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + + return result; + } + + function negate( v, result ) { + result = result || new V2(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + + return result; + } + + function normalize( v, result ) { + result = result || new V2(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + + return result; + } + + function project( v1, v2, result ) { + result = result || new V2(); + + var dp = v1[0]*v2[0] + v1[1]*v2[1]; + var dp_over_v2_squared_length = dp / (v2[0]*v2[0] + v2[1]*v2[1]); - return matrix3; + result[0] = dp_over_v2_squared_length * v2[0]; + result[1] = dp_over_v2_squared_length * v2[1]; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V2(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + + return result; + } + var vector2 = { + add: add, + angle: angle, + clear: clear, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract, + + x: new V2( 1, 0 ), + u: new V2( 1, 0 ), + y: new V2( 0, 1 ), + v: new V2( 0, 1 ), + zero: new V2( 0, 0 ), + one: new V2( 1, 1 ) }; + + return vector2; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('vector/vector2',['require','../../lib/lodash','common/not-implemented','vector/v2','vector/vector2-api','vector/vector'],function ( require ) { -define('matrix/matrix4',['require','./matrix','../vector/vector3'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V2 = require( "vector/v2" )( FLOAT_ARRAY_TYPE ); + var vector2 = require( "vector/vector2-api" )( FLOAT_ARRAY_TYPE ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } - return function( FLOAT_ARRAY_TYPE ) { + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( '../vector/vector3' )( FLOAT_ARRAY_TYPE ); + var Vector2 = function( arg1, arg2 ) { + var argc = arguments.length; - var Matrix4 = function() { - if( 0 === arguments.length ) { - return matrix.$( 16, [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - } else { - return matrix.$( 16, arguments ); - } - }; + this.buffer = new V2( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2 + ); - var matrix4 = { - - $: Matrix4, + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); - add: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) { - return ml[0]; - } else { - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - result = matrix.add(temp, ml[i], result); - temp = result; - } - } - return result; - }, - - subtract: function( ml, result ) { - result = result || Matrix4(); - var temp = ml[0]; - - if (ml.length == 1) - result = temp; - else { - for (var i = 1; i < ml.length; ++ i) { - result = matrix.subtract(temp, ml[i], result); - temp = result; - } - } - return result; - }, + this.modified = true; + this.size = 2; + }; + Vector2.prototype = new Vector(); + Vector2.prototype.constructor = Vector2; - clear: matrix.clear, + function add( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } - equal: matrix.equal, + result = result || this; + vector2.add( this.buffer, other, result.buffer ); + result.modified = true; - multiply: function( ml, result ) { - result = result || Matrix4(); - - if (ml.length == 1) - return ml[0]; - else { - - var temp = ml[0]; - for (var i = 1; i < ml.length; ++ i) { - - result[0] = temp[0]*ml[i][0] + temp[1]*ml[i][4] + temp[2]*ml[i][8] + temp[3]*ml[i][12]; - result[1] = temp[0]*ml[i][1] + temp[1]*ml[i][5] + temp[2]*ml[i][9] + temp[3]*ml[i][13]; - result[2] = temp[0]*ml[i][2] + temp[1]*ml[i][6] + temp[2]*ml[i][10] + temp[3]*ml[i][14]; - result[3] = temp[0]*ml[i][3] + temp[1]*ml[i][7] + temp[2]*ml[i][11] + temp[3]*ml[i][15]; - result[4] = temp[4]*ml[i][0] + temp[5]*ml[i][4] + temp[6]*ml[i][8] + temp[7]*ml[i][12]; - result[5] = temp[4]*ml[i][1] + temp[5]*ml[i][5] + temp[6]*ml[i][9] + temp[7]*ml[i][13]; - result[6] = temp[4]*ml[i][2] + temp[5]*ml[i][6] + temp[6]*ml[i][10] + temp[7]*ml[i][14]; - result[7] = temp[4]*ml[i][3] + temp[5]*ml[i][7] + temp[6]*ml[i][11] + temp[7]*ml[i][15]; - result[8] = temp[8]*ml[i][0] + temp[9]*ml[i][4] + temp[10]*ml[i][8] + temp[11]*ml[i][12]; - result[9] = temp[8]*ml[i][1] + temp[9]*ml[i][5] + temp[10]*ml[i][9] + temp[11]*ml[i][13]; - result[10] = temp[8]*ml[i][2] + temp[9]*ml[i][6] + temp[10]*ml[i][10] + temp[11]*ml[i][14]; - result[11] = temp[8]*ml[i][3] + temp[9]*ml[i][7] + temp[10]*ml[i][11] + temp[11]*ml[i][15]; - result[12] = temp[12]*ml[i][0] + temp[13]*ml[i][4] + temp[14]*ml[i][8] + temp[15]*ml[i][12]; - result[13] = temp[12]*ml[i][1] + temp[13]*ml[i][5] + temp[14]*ml[i][9] + temp[15]*ml[i][13]; - result[14] = temp[12]*ml[i][2] + temp[13]*ml[i][6] + temp[14]*ml[i][10] + temp[15]*ml[i][14]; - result[15] = temp[12]*ml[i][3] + temp[13]*ml[i][7] + temp[14]*ml[i][11] + temp[15]*ml[i][15]; - - temp = result; - } - } - return result; - }, - - multiplyVector3: function( m, v, result ) { - result = result || vector3.$(); - - result[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12]; - result[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13]; - result[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14]; + return this; + } - return result; - }, + function angle( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } - determinant: function (m) { - var a0 = m[0] * m[5] - m[1] * m[4]; - var a1 = m[0] * m[6] - m[2] * m[4]; - var a2 = m[0] * m[7] - m[3] * m[4]; - var a3 = m[1] * m[6] - m[2] * m[5]; - var a4 = m[1] * m[7] - m[3] * m[5]; - var a5 = m[2] * m[7] - m[3] * m[6]; - var b0 = m[8] * m[13] - m[9] * m[12]; - var b1 = m[8] * m[14] - m[10] * m[12]; - var b2 = m[8] * m[15] - m[11] * m[12]; - var b3 = m[9] * m[14] - m[10] * m[13]; - var b4 = m[9] * m[15] - m[11] * m[13]; - var b5 = m[10] * m[15] - m[11] * m[14]; - - var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; - - return det; - }, + return vector2.angle( this.buffer, other ); + } - transpose: function (m , result) { - result = result || Matrix4(); - - result[0] = m[0]; - result[1] = m[4]; - result[2] = m[8]; - result[3] = m[12]; - result[4] = m[1]; - result[5] = m[5]; - result[6] = m[9]; - result[7] = m[13]; - result[8] = m[2]; - result[9] = m[6]; - result[10] = m[10]; - result[11] = m[14]; - result[12] = m[3]; - result[13] = m[7]; - result[14] = m[11]; - result[15] = m[15]; - - return result; - }, + function clear() { + vector2.clear( this.buffer ); + this.modified = true; - inverse: function (m, result) { - - result = result || Matrix4(); - - var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], - a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], - a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], - a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), - invDet; - - // Determinant, throw exception if singular - if (!d) - throw 'matrix is singular'; - - invDet = 1 / d; - result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; - result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; - result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; - result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; - result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; - result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; - result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; - result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; - result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; - result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; - result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; - result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; - result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; - result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; - result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; - result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; - - return result; - }, + return this; + } - toHTML: function( m ) { - var result = "[ "; - for( var i = 0; i < 4; ++ i ) { - result += "
    "; - for( var j = 0; j < 4; ++ j ) { - result += " (" + m[4*i+j] + ") "; - } - } - result += " ]"; - return result; - } + function clone() { + return new Vector2( this ); + } - }; - Object.defineProperty( matrix4, 'zero', { - get: function() { - return Matrix4( [0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'one', { - get: function() { - return Matrix4( [1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1] ); - }, - enumerable: true - }); - - Object.defineProperty( matrix4, 'identity', { - get: function() { - return Matrix4( [1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1] ); - }, - enumerable: true - }); + function dot( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } - return matrix4; + return vector2.dot( this.buffer, other ); + } - }; + function equal( arg ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } -}); + return vector2.equal( this.buffer, other ); + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, -nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + function length() { + return vector2.length( this.buffer ); + } -define('matrix/transform',['require','./matrix4'],function ( require ) { + function multiply( arg, result ) { + result = result || this; + vector2.multiply( this.buffer, arg, result.buffer ); + result.modified = true; - return function( FLOAT_ARRAY_TYPE ) { + return this; + } - var matrix4 = require( './matrix4' )( FLOAT_ARRAY_TYPE ); + function negate( result ) { + result = result || this; + vector2.negate( this.buffer, result.buffer ); + result.modified = true; - var Transform = matrix4.$; - - var transform = { - - $: Transform, + return this; + } - fixed: function( vt, vr, vs ) { - var r = matrix4.identity; + function normalize( result ) { + result = result || this; + vector2.normalize( this.buffer, result.buffer ); + result.modified = true; - if( vt ) { - transform.translate( vt, r ); - } + return this; + } - if( vr ) { - transform.rotate( vr, r ); - } + function project( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } - if( vs ) { - transform.scale( vs, r ); - } + result = result || this; + vector2.project( this.buffer, other, result.buffer ); + result.modified = true; - return r; - }, + return this; + } - // Convert a vector rotation (in radians) to a 4x4 matrix - rotate: function( v, result ) { - var r = result || matrix4.identity; - - var sinA, - cosA; - - var ml; - if( 0 !== v[2] ) { - sinA = Math.sin( v[2] ); - cosA = Math.cos( v[2] ); - ml = []; - ml.push(matrix4.$([ cosA, sinA, 0, 0, - -sinA, cosA, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function set( arg1, arg2 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector2 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + this.modified = true; + } + } else if( 2 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + this.modified = true; + } - if( 0 !== v[1] ) { - sinA = Math.sin( v[1] ); - cosA = Math.cos( v[1] ); - ml = []; - ml.push(matrix4.$([ cosA, 0, -sinA, 0, - 0, 1, 0, 0, - sinA, 0, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + return this; + } - if( 0 !== v[0] ) { - sinA = Math.sin( v[0] ); - cosA = Math.cos( v[0] ); - ml = []; - ml.push(matrix4.$([ 1, 0, 0, 0, - 0, cosA, sinA, 0, - 0, -sinA, cosA, 0, - 0, 0, 0, 1 ] )); - ml.push(matrix4.$(r)); - - matrix4.multiply( ml, r ); - } + function subtract( arg, result ) { + var other; + if( arg instanceof Vector2 ) { + other = arg.buffer; + } else { + other = arg; + } - if( !result ) { - return r; - } - }, + result = result || this; + vector2.subtract( this.buffer, other, result.buffer ); + result.modified = true; - // Convert a vector3 scale to a 4x4 matrix - scale: function( v, result ) { - var r = [ v[0], 0.0, 0.0, 0.0, - 0.0, v[1], 0.0, 0.0, - 0.0, 0.0, v[2], 0.0, - 0.0, 0.0, 0.0, 1.0 ]; + return this; + } + + _.extend( Vector2.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + project: project, + set: set, + subtract: subtract + }); - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - }, + return Vector2; - // Convert a vector3 translation to a 4x4 matrix - translate: function( v, result ) { - var r = [ 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - v[0], v[1], v[2], 1.0 ] + }; - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - } +}); +define('vector/v3',['require','vector/v'],function ( require ) { - }; - - return transform; + var V = require( "vector/v" ); - }; + return function( FLOAT_ARRAY_TYPE ) { -}); + var V3 = function() { + var argc = arguments.length; + var i, j, vi = 0; -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var vector = new FLOAT_ARRAY_TYPE( 3 ); -define('_math',['require','./lang','./constants','./vector/vector2','./vector/vector3','./vector/vector4','./vector/quaternion','./matrix/matrix2','./matrix/matrix3','./matrix/matrix4','./matrix/transform'],function ( require ) { + for( i = 0; i < argc && vi < 3; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 3; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 3; ++ vi ) { + vector[vi] = 0; + } - var lang = require( './lang' ), - constants = require( './constants' ), - vector2 = require( './vector/vector2' ), - vector3 = require( './vector/vector3' ), - vector4 = require( './vector/vector4' ), - quaternion = require( './vector/quaternion' ), - matrix2 = require( './matrix/matrix2' ), - matrix3 = require( './matrix/matrix3' ), - matrix4 = require( './matrix/matrix4' ), - transform = require( './matrix/transform' ); + return vector; + }; + V3.prototype = new V(); + V3.prototype.constructor = V3; - var _Math = function( options ) { - - var _FLOAT_ARRAY_ENUM = { - Float32: Float32Array, - Float64: Float64Array - }; - - var _FLOAT_ARRAY_TYPE = _FLOAT_ARRAY_ENUM.Float32; - - Object.defineProperty( this, 'ARRAY_TYPE', { - get: function() { - return _FLOAT_ARRAY_TYPE; - }, - enumerable: true - }); - - lang.extend( this, constants() ); - - var _vector2 = vector2( _FLOAT_ARRAY_TYPE ); - var _vector3 = vector3( _FLOAT_ARRAY_TYPE ); - var _vector4 = vector4( _FLOAT_ARRAY_TYPE ); - var _quaternion = quaternion( _FLOAT_ARRAY_TYPE ); + return V3; + + }; + +}); +define('matrix/matrix',['require'],function ( require ) { + + var Matrix = function() { + }; + + return Matrix; + +}); +define('vector/vector3-api',['require','common/not-implemented','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + return; + } + + if( undefined === result ) { + result = new V3( v1[0] + v2[0], + v1[1] + v2[1], v1[2] + v2[2] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + + return v; + } + + function cross( v1, v2, result ) { + result = result || new V3(); + + var v1_0 = v1[0], + v1_1 = v1[1], + v1_2 = v1[2]; + var v2_0 = v2[0], + v2_1 = v2[1], + v2_2 = v2[2]; + + result[0] = (v1_1 * v2_2) - (v2_1 * v1_2); + result[1] = (v1_2 * v2_0) - (v2_2 * v1_0); + result[2] = (v1_0 * v2_1) - (v2_0 * v1_1); + + return result; + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V3(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + + return result; + } + + function negate( v, result ) { + result = result || new V3(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + + return result; + } + + function normalize( v, result ) { + result = result || new V3(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V3(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + + return result; + } + + //This does a matrix3 by vector3 transform, which is a matrix multiplication + //The matrix3 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V3(); + + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[1] * y + m[2] * z; + result[1] = m[3] * x + m[4] * y + m[5] * z; + result[2] = m[6] * x + m[7] * y + m[8] * z; + + return result; + } + + var vector3 = { + add: add, + angle: angle, + clear: clear, + cross: cross, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V3( 1, 0, 0 ), + y: new V3( 0, 1, 0 ), + z: new V3( 0, 0, 1 ), + zero: new V3( 0, 0, 0 ), + one: new V3( 1, 1, 1 ) + }; + + return vector3; + + }; + +}); +define('vector/vector3',['require','../../lib/lodash','common/not-implemented','vector/v3','vector/vector3-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + var vector3 = require( "vector/vector3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector3 = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + + this.buffer = new V3( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + + this.modified = true; + this.size = 3; + }; + Vector3.prototype = new Vector(); + Vector3.prototype.constructor = Vector3; + + function add( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.angle( this.buffer, other ); + } + + function clear() { + vector3.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector3( this ); + } + + function cross( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.cross( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function dot( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector3.equal( this.buffer, other ); + } + + function length() { + return vector3.length( this.buffer ); + } + + function multiply( arg, result ) { + result = result || this; + vector3.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector3.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector3.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector3 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + this.modified = true; + } + } else if( 3 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector3.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector3.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + cross: cross, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector3; + + }; + +}); +define('vector/v4',['require','vector/v'],function ( require ) { + + var V = require( "vector/v" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var V4 = function() { + var argc = arguments.length; + var i, j, vi = 0; + + var vector = new FLOAT_ARRAY_TYPE( 4 ); + + for( i = 0; i < argc && vi < 4; ++ i ) { + var arg = arguments[i]; + if( arg === undefined ) { + break; + } else if( arg instanceof Array || + arg instanceof FLOAT_ARRAY_TYPE ) { + for( j = 0; j < arg.length && vi < 4; ++ j ) { + vector[vi ++] = arg[j]; + } + } else { + vector[vi ++] = arg; + } + } + // Fill in missing elements with zero + for( ; vi < 4; ++ vi ) { + vector[vi] = 0; + } + + return vector; + }; + V4.prototype = new V(); + V4.prototype.constructor = V4; + + return V4; + + }; + +}); +define('vector/vector4-api',['require','common/not-implemented','vector/v4'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + + function add( v1, v2, result ) { + if( result === v1 ) { + v1[0] += v2[0]; + v1[1] += v2[1]; + v1[2] += v2[2]; + v1[3] += v2[3]; + return; + } + + if( undefined === result ) { + result = new V4( v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3] ); + return result; + } else { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + result[3] = v1[3] + v2[3]; + return; + } + } + + function angle( v1, v2 ) { + return Math.acos( + (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / + (Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] + v1[3] * v1[3]) * + Math.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2] + v2[3] * v2[3]) ) ); + } + + function clear( v ) { + v[0] = 0; + v[1] = 0; + v[2] = 0; + v[3] = 0; + + return v; + } + + function dot( v1, v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3]; + } + + function equal( v1, v2, e ) { + e = e || 0.000001; + + if( v1.length !== v2.length ) { + return false; + } + + var d0 = Math.abs( v1[0] - v2[0] ); + var d1 = Math.abs( v1[1] - v2[1] ); + var d2 = Math.abs( v1[2] - v2[2] ); + var d3 = Math.abs( v1[3] - v2[3] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } + + return true; + } + + function length( v ) { + var r = 0; + + r += v[0] * v[0]; + r += v[1] * v[1]; + r += v[2] * v[2]; + r += v[2] * v[2]; + + return Math.sqrt( r ); + } + + function multiply( v, s, result ) { + result = result || new V4(); + + result[0] = s * v[0]; + result[1] = s * v[1]; + result[2] = s * v[2]; + result[3] = s * v[3]; + + return result; + } + + function negate( v, result ) { + result = result || new V4(); + + result[0] = -1 * v[0]; + result[1] = -1 * v[1]; + result[2] = -1 * v[2]; + result[3] = -1 * v[3]; + + return result; + } + + function normalize( v, result ) { + result = result || new V4(); + var l = length( v ); + + result[0] = v[0]/l; + result[1] = v[1]/l; + result[2] = v[2]/l; + result[3] = v[3]/l; + + return result; + } + + function set( v ) { + if( 2 === arguments.length ) { + v[0] = arguments[1][0]; + v[1] = arguments[1][1]; + v[2] = arguments[1][2]; + v[3] = arguments[1][3]; + } else { + v[0] = arguments[1]; + v[1] = arguments[2]; + v[2] = arguments[3]; + v[3] = arguments[4]; + } + + return v; + } + + function subtract( v1, v2, result ) { + result = result || new V4(); + + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + result[3] = v1[3] - v2[3]; + + return result; + } + + //This does a matrix4 by vector4 transform, which is a matrix multiplication + //The matrix4 is on the left side of the multiplication and is multiplied by + // the vector in column form + function transform( v, m, result ) { + result = result || new V4(); + + var x = v[0], y = v[1], z = v[2], w = v[3]; + + result[0] = m[0] * x + m[1] * y + m[2] * z + m[3] * w; + result[1] = m[4] * x + m[5] * y + m[6] * z + m[7] * w; + result[2] = m[8] * x + m[9] * y + m[10] * z + m[11] * w; + result[3] = m[12] * x + m[13] * y + m[14] * z + m[15] * w; + + return result; + } + + var vector4 = { + add: add, + angle: angle, + clear: clear, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + limit: notImplemented, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform, + + x: new V4( 1, 0, 0, 0 ), + y: new V4( 0, 1, 0, 0 ), + z: new V4( 0, 0, 1, 0 ), + w: new V4( 0, 0, 0, 1 ), + zero: new V4( 0, 0, 0, 0 ), + one: new V4( 1, 1, 1, 1 ) + }; + + return vector4; + + }; + +}); +define('vector/vector4',['require','../../lib/lodash','common/not-implemented','vector/v4','vector/vector4-api','matrix/matrix','vector/vector'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var V4 = require( "vector/v4" )( FLOAT_ARRAY_TYPE ); + var vector4 = require( "vector/vector4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + var Vector = require( "vector/vector" ); + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.modified = true; + } + + var Vector4 = function( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + + this.buffer = new V4( + (arg1 instanceof Vector) ? arg1.buffer : arg1, + (arg2 instanceof Vector) ? arg2.buffer : arg2, + (arg3 instanceof Vector) ? arg3.buffer : arg3, + (arg4 instanceof Vector) ? arg4.buffer : arg4 + ); + + Object.defineProperties( this, { + x: { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + y: { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + z: { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + w: { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + + this.modified = true; + this.size = 4; + }; + Vector4.prototype = new Vector(); + Vector4.prototype.constructor = Vector4; + + function add( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function angle( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.angle( this.buffer, other ); + } + + function clear() { + vector4.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Vector4( this ); + } + + function dot( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.dot( this.buffer, other ); + } + + function equal( arg ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return vector4.equal( this.buffer, other ); + } + + function length() { + return vector4.length( this.buffer ); + } + + function multiply( arg, result ) { + result = result || this; + vector4.multiply( this.buffer, arg, result.buffer ); + result.modified = true; + + return this; + } + + function negate( result ) { + result = result || this; + vector4.negate( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function normalize( result ) { + result = result || this; + vector4.normalize( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4 ) { + var argc = arguments.length; + var buffer = this.buffer; + if( 1 === argc ) { + if( arg1 instanceof Vector4 ) { + var other = arg1.buffer; + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + this.modified = true; + } else { + buffer[0] = arg1[0]; + buffer[1] = arg1[1]; + buffer[2] = arg1[2]; + buffer[3] = arg1[3]; + this.modified = true; + } + } else if( 4 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Vector4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transform( arg, result ) { + var other; + if( arg instanceof Matrix ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + vector4.transform( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Vector4.prototype, { + add: add, + angle: angle, + clear: clear, + clone: clone, + distance: notImplemented, + dot: dot, + equal: equal, + length: length, + multiply: multiply, + negate: negate, + normalize: normalize, + set: set, + subtract: subtract, + transform: transform + }); + + return Vector4; + + }; + +}); +define('matrix/m',['require'],function ( require ) { + + var M = function() { + }; + + return M; + +}); +define('matrix/m2',['require','matrix/m'],function ( require ) { + + var M = require( "matrix/m" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var M2 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, + 0, 0]; + } else { + elements = arguments; + } + + var matrix = new FLOAT_ARRAY_TYPE( 4 ); + for( var i = 0; i < 4; ++ i ) { + matrix[i] = elements[i]; + } + + return matrix; + }; + M2.prototype = new M(); + M2.prototype.constructor = M2; + + return M2; + + }; + +}); +/*! + * Lo-Dash v0.4.1 + * Copyright 2012 John-David Dalton + * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. + * + * Available under MIT license + */ +;(function(window, undefined) { + + + /** + * Used to cache the last `_.templateSettings.evaluate` delimiter to avoid + * unnecessarily assigning `reEvaluateDelimiter` a new generated regexp. + * Assigned in `_.template`. + */ + var lastEvaluateDelimiter; + + /** + * Used to cache the last template `options.variable` to avoid unnecessarily + * assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`. + */ + var lastVariable; + + /** + * Used to match potentially incorrect data object references, like `obj.obj`, + * in compiled templates. Assigned in `_.template`. + */ + var reDoubleVariable; + + /** + * Used to match "evaluate" delimiters, including internal delimiters, + * in template text. Assigned in `_.template`. + */ + var reEvaluateDelimiter; + + /** Detect free variable `exports` */ + var freeExports = typeof exports == 'object' && exports && + (typeof global == 'object' && global && global == global.global && (window = global), exports); + + /** Native prototype shortcuts */ + var ArrayProto = Array.prototype, + ObjectProto = Object.prototype; + + /** Used to generate unique IDs */ + var idCounter = 0; + + /** Used to restore the original `_` reference in `noConflict` */ + var oldDash = window._; + + /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ + var reComplexDelimiter = /[-+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + + /** Used to match empty string literals in compiled template source */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to insert the data object variable into compiled template source */ + var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + (ObjectProto.valueOf + '') + .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') + .replace(/valueOf|for [^\]]+/g, '.+?') + '$' + ); + + /** Used to match tokens in template text */ + var reToken = /__token__(\d+)/g; + + /** Used to match unescaped characters in strings for inclusion in HTML */ + var reUnescapedHtml = /[&<"']/g; + + /** Used to match unescaped characters in compiled string literals */ + var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowed = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** Used to make template sourceURLs easier to identify */ + var templateCounter = 0; + + /** Used to replace template delimiters */ + var token = '__token__'; + + /** Used to store tokenized template text snippets */ + var tokenized = []; + + /** Native method shortcuts */ + var concat = ArrayProto.concat, + hasOwnProperty = ObjectProto.hasOwnProperty, + push = ArrayProto.push, + propertyIsEnumerable = ObjectProto.propertyIsEnumerable, + slice = ArrayProto.slice, + toString = ObjectProto.toString; + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeIsFinite = window.isFinite, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; + + /** `Object#toString` result shortcuts */ + var arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + funcClass = '[object Function]', + numberClass = '[object Number]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Timer shortcuts */ + var clearTimeout = window.clearTimeout, + setTimeout = window.setTimeout; + + /** + * Detect the JScript [[DontEnum]] bug: + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well. + */ + var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + + /** Detect if `Array#slice` cannot be used to convert strings to arrays (e.g. Opera < 10.52) */ + var noArraySliceOnStrings = slice.call('x')[0] != 'x'; + + /** + * Detect lack of support for accessing string characters by index: + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + */ + var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + + /* Detect if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */ + var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); + + /* Detect if `Object.keys` exists and is inferred to be fast (i.e. V8, Opera, IE) */ + var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); + + /** Detect if sourceURL syntax is usable without erroring */ + try { + // Adobe's and Narwhal's JS engines will error + var useSourceURL = (Function('//@')(), true); + } catch(e){ } + + /** + * Used to escape characters for inclusion in HTML. + * The `>` and `/` characters don't require escaping in HTML and have no + * special meaning unless they're part of a tag or an unquoted attribute value + * http://mathiasbynens.be/notes/ambiguous-ampersands (semi-related fun fact) + */ + var htmlEscapes = { + '&': '&', + '<': '<', + '"': '"', + "'": ''' + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used to escape characters for inclusion in compiled string literals */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The `lodash` function. + * + * @name _ + * @constructor + * @param {Mixed} value The value to wrap in a `LoDash` instance. + * @returns {Object} Returns a `LoDash` instance. + */ + function lodash(value) { + // allow invoking `lodash` without the `new` operator + return new LoDash(value); + } + + /** + * Creates a `LoDash` instance that wraps a value to allow chaining. + * + * @private + * @constructor + * @param {Mixed} value The value to wrap. + */ + function LoDash(value) { + // exit early if already wrapped + if (value && value._wrapped) { + return value; + } + this._wrapped = value; + } + + /** + * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, + * change the following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'escape': /<%-([\s\S]+?)%>/g, + + /** + * Used to detect code to be evaluated. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'evaluate': /<%([\s\S]+?)%>/g, + + /** + * Used to detect `data` property values to inject. + * + * @static + * @memberOf _.templateSettings + * @type RegExp + */ + 'interpolate': /<%=([\s\S]+?)%>/g, + + /** + * Used to reference the data object in the template text. + * + * @static + * @memberOf _.templateSettings + * @type String + */ + 'variable': 'obj' + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Obect} data The data object used to populate the text. + * @returns {String} Returns the interpolated text. + */ + var iteratorTemplate = template( + // assign the `result` variable an initial value + 'var result<% if (init) { %> = <%= init %><% } %>;\n' + + // add code to exit early or do so if the first argument is falsey + '<%= exit %>;\n' + + // add code after the exit snippet but before the iteration branches + '<%= top %>;\n' + + 'var index, iteratee = <%= iteratee %>;\n' + + + // the following branch is for iterating arrays and array-like objects + '<% if (arrayBranch) { %>' + + 'var length = iteratee.length; index = -1;' + + ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + + // add support for accessing string characters by index if needed + ' <% if (noCharByIndex) { %>\n' + + ' if (toString.call(iteratee) == stringClass) {\n' + + ' iteratee = iteratee.split(\'\')\n' + + ' }' + + ' <% } %>\n' + + + ' <%= arrayBranch.beforeLoop %>;\n' + + ' while (++index < length) {\n' + + ' <%= arrayBranch.inLoop %>\n' + + ' }' + + ' <% if (objectBranch) { %>\n}<% } %>' + + '<% } %>' + + + // the following branch is for iterating an object's own/inherited properties + '<% if (objectBranch) { %>' + + ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + + ' var skipProto = typeof iteratee == \'function\' && \n' + + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + + ' <% } %>' + + + // iterate own properties using `Object.keys` if it's fast + ' <% if (isKeysFast && useHas) { %>\n' + + ' var props = nativeKeys(iteratee),\n' + + ' propIndex = -1,\n' + + ' length = props.length;\n\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' while (++propIndex < length) {\n' + + ' index = props[propIndex];\n' + + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' }' + + + // else using a for-in loop + ' <% } else { %>\n' + + ' <%= objectBranch.beforeLoop %>;\n' + + ' for (index in iteratee) {' + + ' <% if (hasDontEnumBug) { %>\n' + + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' <%= objectBranch.inLoop %>;\n' + + ' <% if (useHas) { %>}<% } %>' + + ' <% } else { %>\n' + + + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + // (if the prototype or a property on the prototype has been set) + // incorrectly sets a function's `prototype` property [[Enumerable]] + // value to `true`. Because of this Lo-Dash standardizes on skipping + // the the `prototype` property of functions regardless of its + // [[Enumerable]] value. + ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>\n' + + ' }' + + ' <% } %>' + + + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an + // existing property and the `constructor` property of a prototype + // defaults to non-enumerable, Lo-Dash skips the `constructor` + // property when it infers it's iterating over a `prototype` object. + ' <% if (hasDontEnumBug) { %>\n\n' + + ' var ctor = iteratee.constructor;\n' + + ' <% for (var k = 0; k < 7; k++) { %>\n' + + ' index = \'<%= shadowed[k] %>\';\n' + + ' if (<%' + + ' if (shadowed[k] == \'constructor\') {' + + ' %>!(ctor && ctor.prototype === iteratee) && <%' + + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }' + + ' <% } %>' + + ' <% } %>' + + ' <% if (arrayBranch) { %>\n}<% } %>' + + '<% } %>\n' + + + // add code to the bottom of the iteration function + '<%= bottom %>;\n' + + // finally, return the `result` + 'return result' + ); + + /** + * Reusable iterator options shared by + * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, + * `reject`, `some`, and `sortBy`. + */ + var baseIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'init': 'collection', + 'top': + 'if (!callback) {\n' + + ' callback = identity\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': 'callback(iteratee[index], index, collection)' + }; + + /** Reusable iterator options for `every` and `some` */ + var everyIteratorOptions = { + 'init': 'true', + 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + }; + + /** Reusable iterator options for `defaults` and `extend` */ + var extendIteratorOptions = { + 'args': 'object', + 'init': 'object', + 'top': + 'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' + + ' source = arguments[sourceIndex];\n' + + (hasDontEnumBug ? ' if (source) {' : ''), + 'iteratee': 'source', + 'useHas': false, + 'inLoop': 'result[index] = iteratee[index]', + 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + }; + + /** Reusable iterator options for `filter` and `reject` */ + var filterIteratorOptions = { + 'init': '[]', + 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + }; + + /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ + var forEachIteratorOptions = { + 'top': 'if (thisArg) callback = iteratorBind(callback, thisArg)' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'inLoop': { + 'object': baseIteratorOptions.inLoop + } + }; + + /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ + var mapIteratorOptions = { + 'init': '', + 'exit': 'if (!collection) return []', + 'beforeLoop': { + 'array': 'result = Array(length)', + 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') + }, + 'inLoop': { + 'array': 'result[index] = callback(iteratee[index], index, collection)', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + } + }; + + /*--------------------------------------------------------------------------*/ + + /** + * Creates compiled iteration functions. The iteration function will be created + * to iterate over only objects if the first argument of `options.args` is + * "object" or `options.inLoop.array` is falsey. + * + * @private + * @param {Object} [options1, options2, ...] The compile options objects. + * + * args - A string of comma separated arguments the iteration function will + * accept. + * + * init - A string to specify the initial value of the `result` variable. + * + * exit - A string of code to use in place of the default exit-early check + * of `if (!arguments[0]) return result`. + * + * top - A string of code to execute after the exit-early check but before + * the iteration branches. + * + * beforeLoop - A string or object containing an "array" or "object" property + * of code to execute before the array or object loops. + * + * iteratee - A string or object containing an "array" or "object" property + * of the variable to be iterated in the loop expression. + * + * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks + * in the object loop. + * + * inLoop - A string or object containing an "array" or "object" property + * of code to execute in the array or object loops. + * + * bottom - A string of code to execute after the iteration branches but + * before the `result` is returned. + * + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + var object, + prop, + value, + index = -1, + length = arguments.length; + + // merge options into a template data object + var data = { + 'bottom': '', + 'exit': '', + 'init': '', + 'top': '', + 'arrayBranch': { 'beforeLoop': '' }, + 'objectBranch': { 'beforeLoop': '' } + }; + + while (++index < length) { + object = arguments[index]; + for (prop in object) { + value = (value = object[prop]) == null ? '' : value; + // keep this regexp explicit for the build pre-process + if (/beforeLoop|inLoop/.test(prop)) { + if (typeof value == 'string') { + value = { 'array': value, 'object': value }; + } + data.arrayBranch[prop] = value.array; + data.objectBranch[prop] = value.object; + } else { + data[prop] = value; + } + } + } + // set additional template `data` values + var args = data.args, + firstArg = /^[^,]+/.exec(args)[0], + iteratee = (data.iteratee = data.iteratee || firstArg); + + data.firstArg = firstArg; + data.hasDontEnumBug = hasDontEnumBug; + data.isKeysFast = isKeysFast; + data.shadowed = shadowed; + data.useHas = data.useHas !== false; + + if (!('noCharByIndex' in data)) { + data.noCharByIndex = noCharByIndex; + } + if (!data.exit) { + data.exit = 'if (!' + firstArg + ') return result'; + } + if (firstArg != 'collection' || !data.arrayBranch.inLoop) { + data.arrayBranch = null; + } + // create the function factory + var factory = Function( + 'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' + + 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' + + 'slice, stringClass, toString', + ' return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + ); + // return the compiled function + return factory( + arrayClass, compareAscending, funcClass, hasOwnProperty, identity, + iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, + stringClass, toString + ); + } + + /** + * Used by `sortBy` to compare transformed values of `collection`, sorting + * them in ascending order. + * + * @private + * @param {Object} a The object to compare to `b`. + * @param {Object} b The object to compare to `a`. + * @returns {Number} Returns `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + */ + function compareAscending(a, b) { + a = a.criteria; + b = b.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + } + + /** + * Used by `template` to replace tokens with their corresponding code snippets. + * + * @private + * @param {String} match The matched token. + * @param {String} index The `tokenized` index of the code snippet. + * @returns {String} Returns the code snippet. + */ + function detokenize(match, index) { + return tokenized[index]; + } + + /** + * Used by `template` to escape characters for inclusion in compiled + * string literals. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeStringChar(match) { + return '\\' + stringEscapes[match]; + } + + /** + * Used by `escape` to escape characters for inclusion in HTML. + * + * @private + * @param {String} match The matched character to escape. + * @returns {String} Returns the escaped character. + */ + function escapeHtmlChar(match) { + return htmlEscapes[match]; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and the arguments (value, index, object). + * + * @private + * @param {Function} func The function to bind. + * @param {Mixed} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new bound function. + */ + function iteratorBind(func, thisArg) { + return function(value, index, object) { + return func.call(thisArg, value, index, object); + }; + } + + /** + * A no-operation function. + * + * @private + */ + function noop() { + // no operation performed + } + + /** + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. + * + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' + }); + + /** + * Used by `template` to replace "escape" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEscape(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n__e(" + value + ") +\n'"; + return token + index; + } + + /** + * Used by `template` to replace "evaluate" template delimiters, or complex + * "escape" and "interpolate" delimiters, with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @param {String} escapeValue The "escape" delimiter value. + * @param {String} interpolateValue The "interpolate" delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + var index = tokenized.length; + if (value) { + tokenized[index] = "';\n" + value + ";\n__p += '" + } else if (escapeValue) { + tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (interpolateValue) { + tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; + } + return token + index; + } + + /** + * Used by `template` to replace "interpolate" template delimiters with tokens. + * + * @private + * @param {String} match The matched template delimiter. + * @param {String} value The delimiter value. + * @returns {String} Returns a token. + */ + function tokenizeInterpolate(match, value) { + if (reComplexDelimiter.test(value)) { + return ''; + } + var index = tokenized.length; + tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'"; + return token + index; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` value is present in a `collection` using strict + * equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @example + * + * _.contains([1, 2, 3], 3); + * // => true + * + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true + * + * _.contains('curly', 'ur'); + * // => true + */ + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (iteratee[index] === target) return true' + }); + + /** + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + */ + var every = createIterator(baseIteratorOptions, everyIteratorOptions); + + /** + * Examines each value in a `collection`, returning an array of all values the + * `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; for arrays they are (value, index, array) and for + * objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that passed callback check. + * @example + * + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] + */ + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + + /** + * Examines each value in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * value, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. + * @example + * + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 + */ + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + }); + + /** + * Iterates over a `collection`, executing the `callback` for each value in the + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array|Object} Returns the `collection`. + * @example + * + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) + */ + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; for arrays they are (value, index, array) and for objects they + * are (value, key, object). The `callback` argument may also be the name of a + * property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(iteratee[index], index, collection)\n' + + ' : iteratee[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + }); + + /** + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + + '.apply(iteratee[index], args)', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + + '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' + } + }); + + /** + * Produces a new array of values by mapping each element in the `collection` + * through a transformation `callback`. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; for arrays they are (value, index, array) + * and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values returned by the callback. + * @example + * + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) + */ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); + + /** + * Retrieves the value of a specified property from all elements in + * the `collection`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] + */ + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = iteratee[index][property]', + 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' + } + }); + + /** + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index, array) and for + * objects they are (accumulator, value, key, object). + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 + */ + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', + 'top': + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, iteratee[index], index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, iteratee[index])\n' + + ' : callback(result, iteratee[index], index, collection)' + } + }); + + /** + * The right-associative version of `_.reduce`. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. + * @example + * + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } + + var length = collection.length, + noaccum = arguments.length < 3; + + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + if (length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; + + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); + } + return accumulator; + } + + var prop, + props = keys(collection); + + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; + } + + /** + * The opposite of `_.filter`, this method returns the values of a `collection` + * that `callback` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @example + * + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] + */ + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); + + /** + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; for arrays they are + * (value, index, array) and for objects they are (value, key, object). + * + * @static + * @memberOf _ + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @example + * + * _.some([null, 0, 'yes', false]); + * // => true + */ + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); + + + /** + * Produces a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a transformation `callback`. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; + * for arrays they are (value, index, array) and for objects they are + * (value, key, object). The `callback` argument may also be the name of a + * property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + 'top': + 'if (typeof callback == \'string\') {\n' + + ' var prop = callback;\n' + + ' callback = function(collection) { return collection[prop] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + + ' criteria: callback(iteratee[index], index, collection),\n' + + ' value: iteratee[index]\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); + + /** + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. + * @example + * + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] + */ + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && toString.call(collection.toArray) == funcClass) { + return collection.toArray(); + } + var length = collection.length; + if (length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Produces a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (array[index]) { + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array of `array` values not present in the other arrays + * using strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` values not present in the + * other arrays. + * @example + * + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] + */ + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments); + + while (++index < length) { + if (indexOf(flattened, array[index], length) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Gets the first value of the `array`. Pass `n` to return the first `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first value or an array of the first `n` values + * of `array`. + * @example + * + * _.first([5, 4, 3, 2, 1]); + * // => 5 + */ + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } + } + + /** + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. + * @example + * + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; + */ + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } + + /** + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 + */ + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Gets all but the last value of `array`. Pass `n` to exclude the last `n` + * values from the result. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last value or `n` values of `array`. + * @example + * + * _.initial([3, 2, 1]); + * // => [3, 2] + */ + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } + + /** + * Computes the intersection of all the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in **all** of the arrays. + * @example + * + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] + */ + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1); + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other) { return indexOf(other, value) > -1; })) { + result.push(value); + } + } + return result; + } + + /** + * Gets the last value of the `array`. Pass `n` to return the lasy `n` values + * of the `array`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last value or an array of the last `n` values + * of `array`. + * @example + * + * _.last([3, 2, 1]); + * // => 1 + */ + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } + + /** + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. + * @example + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; + */ + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. + * @example + * + * _.min([10, 5, 100, 2, 1000]); + * // => 2 + */ + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } + + /** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Number} [start=0] The start of the range. + * @param {Number} end The end of the range. + * @param {Number} [step=1] The value to increment or descrement by. + * @returns {Array} Returns a new range array. + * @example + * + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] + */ + function range(start, end, step) { + step || (step = 1); + if (end == null) { + end = start || 0; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; + } + + /** + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. + * + * @static + * @memberOf _ + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. + * @example + * + * _.rest([3, 2, 1]); + * // => [2, 1] + */ + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); + } + + /** + * Produces a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] + */ + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; + } + + /** + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. + * @example + * + * _.sortedIndex([20, 30, 40], 35); + * // => 2 + * + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 + */ + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; + } + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } + + /** + * Computes the union of the passed-in arrays. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. + * @example + * + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] + */ + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; + + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } + } + return result; + } + + /** + * Produces a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, + * each value of `array` is passed through a transformation `callback` before + * uniqueness is computed. The `callback` is bound to `thisArg` and invoked + * with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; + + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; + } + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); + } + } + return result; + } + + /** + * Produces a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length; + + while (++index < length) { + if (indexOf(arguments, array[index], 1) < 0) { + result.push(array[index]); + } + } + return result; + } + + /** + * Merges the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of merged arrays. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); + + while (++index < length) { + result[index] = pluck(arguments, index); + } + return result; + } + + /** + * Merges an array of `keys` and an array of `values` into a single object. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} keys The array of keys. + * @param {Array} [values=[]] The array of values. + * @returns {Object} Returns an object composed of the given keys and + * corresponding values. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a new function that is restricted to executing only after it is + * called `n` times. + * + * @static + * @memberOf _ + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved + */ + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' + */ + function bind(func, thisArg) { + var methodName, + isFunc = toString.call(func) == funcClass; + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } + + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; + } + + /** + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. + * + * @static + * @memberOf _ + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns the `object`. + * @example + * + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; + * + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value + */ + function bindAll(object) { + var funcs = arguments, + index = 1; + + if (funcs.length == 1) { + index = 0; + funcs = functions(object); + } + for (var length = funcs.length; index < length; index++) { + object[funcs[index]] = bind(object[funcs[index]], object); + } + return object; + } + + /** + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. + * @example + * + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' + */ + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; + } + + /** + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); + */ + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } + + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; + } + + /** + * Executes the `func` function after `wait` milliseconds. Additional arguments + * are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) + */ + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); + } + + /** + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments are passed to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. + * @example + * + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called + */ + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); + } + + /** + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); + */ + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; + } + + /** + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. + */ + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + return result; + }; + } + + /** + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the partially applied + * function. This method is similar `bind`, except it does **not** alter the + * `this` binding. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' + */ + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; + + return function() { + var result, + others = arguments; + + if (others.length) { + args.length = argsLength; + push.apply(args, others); + } + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; + } + + /** + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. + * @example + * + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); + */ + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); + } + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; + } + + /** + * Create a new function that passes the `func` function to the `wrapper` + * function as its first argument. Additional arguments are appended to those + * passed to the `wrapper` function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to wrap. + * @param {Function} wrapper The wrapper function. + * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. + * @returns {Function} Returns the new function. + * @example + * + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' + */ + function wrap(func, wrapper) { + return function() { + var args = [func]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a shallow clone of the `value`. Any nested objects or arrays will be + * assigned by reference and not cloned. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to clone. + * @returns {Mixed} Returns the cloned `value`. + * @example + * + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' }; + */ + function clone(value) { + return value && objectTypes[typeof value] + ? (isArray(value) ? value.slice() : extend({}, value)) + : value; + } + + /** + * Assigns missing properties on `object` with default values from the defaults + * objects. Once a property is set, additional defaults of the same property + * will be ignored. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to populate. + * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. + * @returns {Object} Returns `object`. + * @example + * + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Copies enumerable properties from the source objects to the `destination` object. + * Subsequent sources will overwrite propery assignments of previous sources. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var extend = createIterator(extendIteratorOptions); + + /** + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) + */ + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) + */ + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + + /** + * Produces a sorted array of the enumerable properties, own and inherited, + * of `object` that have function values. + * + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. + * @example + * + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + var functions = createIterator({ + 'args': 'object', + 'init': '[]', + 'useHas': false, + 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', + 'bottom': 'result.sort()' + }); + + /** + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @example + * + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true + */ + function has(object, property) { + return hasOwnProperty.call(object, property); + } + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = function(value) { + return toString.call(value) == '[object Arguments]'; + }; + // fallback for browser like IE < 9 which detect `arguments` as `[object Object]` + if (!isArguments(arguments)) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; + + /** + * Checks if `value` is a boolean (`true` or `false`) value. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @example + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } + + /** + * Checks if `value` is a date. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + */ + function isDate(value) { + return toString.call(value) == dateClass; + } + + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } + + /** + * Checks if `value` is empty. Arrays or strings with a length of `0` and + * objects with no own enumerable properties are considered "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', + 'top': + 'var className = toString.call(value);\n' + + 'if (className == arrayClass || className == stringClass) return !value.length', + 'inLoop': { + 'object': 'return false' + } + }); + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack] Internally used to keep track of "seen" objects to + * avoid circular references. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @example + * + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true + */ + function isEqual(a, b, stack) { + stack || (stack = []); + + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // a strict comparison is necessary because `undefined == null` + if (a == null || b == null) { + return a === b; + } + // unwrap any wrapped objects + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // invoke a custom `isEqual` method if one is provided + if (a.isEqual && toString.call(a.isEqual) == funcClass) { + return a.isEqual(b); + } + if (b.isEqual && toString.call(b.isEqual) == funcClass) { + return b.isEqual(a); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + // strings, numbers, dates, and booleans are compared by value + case stringClass: + // primitives and their corresponding object instances are equivalent; + // thus, `'5'` is quivalent to `new String('5')` + return a == String(b); + + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); + + case boolClass: + case dateClass: + // coerce dates and booleans to numeric values, dates to milliseconds and booleans to 1 or 0; + // treat invalid dates coerced to `NaN` as not equal + return +a == +b; + + // regexps are compared by their source and flags + case regexpClass: + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) { + return true; + } + } + + var index = -1, + result = true, + size = 0; + + // add the first collection to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays + if (className == arrayClass) { + // compare array lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack))) { + break; + } + } + } + } else { + // objects with different constructors are not equivalent + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { + return false; + } + // deep compare objects. + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + // ensure both objects have the same number of properties + if (result) { + for (prop in b) { + // Adobe's JS engine, embedded in applications like InDesign, has a + // bug that causes `!size--` to throw an error so it must be wrapped + // in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + break; + } + } + result = !size; + } + // handle JScript [[DontEnum]] bug + if (result && hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop)) { + if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { + break; + } + } + } + } + } + // remove the first collection from the stack of traversed objects + stack.pop(); + return result; + } + + /** + * Checks if `value` is a finite number. + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @example + * + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false + */ + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(''.concat); + * // => true + */ + function isFunction(value) { + return toString.call(value) == funcClass; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + return value && objectTypes[typeof value]; + } + + /** + * Checks if `value` is `NaN`. + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // `NaN` as a primitive is the only value that is not equal to itself + // (perform the [[Class]] check first to avoid errors with some host objects in IE) + return toString.call(value) == numberClass && value != +value + } + + /** + * Checks if `value` is `null`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is a number. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @example + * + * _.isNumber(8.4 * 5; + * // => true + */ + function isNumber(value) { + return toString.call(value) == numberClass; + } + + /** + * Checks if `value` is a regular expression. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @example + * + * _.isRegExp(/moe/); + * // => true + */ + function isRegExp(value) { + return toString.call(value) == regexpClass; + } + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('moe'); + * // => true + */ + function isString(value) { + return toString.call(value) == stringClass; + } + + /** + * Checks if `value` is `undefined`. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Produces an array of object`'s own enumerable property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; + + /** + * Creates an object composed of the specified properties. Property names may + * be specified as individual arguments or as arrays of property names. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to pluck. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; + + // start `index` at `1` to skip `object` + while (++index < length) { + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; + } + } + return result; + } + + /** + * Gets the size of `value` by returning `value.length` if `value` is a string + * or array, or the number of own enumerable properties if `value` is an object. + * + * @deprecated + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` if `value` is a string or array, + * or the number of own enumerable properties if `value` is an object. + * @example + * + * _.size([1, 2]); + * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 + */ + function size(value) { + if (!value) { + return 0; + } + var length = value.length; + return length === length >>> 0 ? value.length : keys(value).length; + } + + /** + * Produces an array of `object`'s own enumerable property values. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. + * @example + * + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(iteratee[index])' + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` + * characters. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} string The string to escape. + * @returns {String} Returns the escaped string. + * @example + * + * _.escape('Curly, Larry & Moe'); + * // => "Curly, Larry & Moe" + */ + function escape(string) { + return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + } + + /** + * This function returns the first argument passed to it. + * Note: It is used throughout Lo-Dash as a default callback. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Mixed} value Any value. + * @returns {Mixed} Returns `value`. + * @example + * + * var moe = { 'name': 'moe' }; + * moe === _.identity(moe); + * // => true + */ + function identity(value) { + return value; + } + + /** + * Adds functions properties of `object` to the `lodash` function and chainable + * wrapper. + * + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object of function properties to add to `lodash`. + * @example + * + * _.mixin({ + * 'capitalize': function(string) { + * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + * } + * }); + * + * _.capitalize('curly'); + * // => 'Curly' + * + * _('larry').capitalize(); + * // => 'Larry' + */ + function mixin(object) { + forEach(functions(object), function(methodName) { + var func = lodash[methodName] = object[methodName]; + + LoDash.prototype[methodName] = function() { + var args = [this._wrapped]; + if (arguments.length) { + push.apply(args, arguments); + } + var result = func.apply(lodash, args); + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + } + + /** + * Reverts the '_' variable to its previous value and returns a reference to + * the `lodash` function. + * + * @static + * @memberOf _ + * @category Utilities + * @returns {Function} Returns the `lodash` function. + * @example + * + * var lodash = _.noConflict(); + */ + function noConflict() { + window._ = oldDash; + return this; + } + + /** + * Resolves the value of `property` on `object`. If `property` is a function + * it will be invoked and its result returned, else the property value is + * returned. If `object` is falsey, then `null` is returned. + * + * @deprecated + * @static + * @memberOf _ + * @category Utilities + * @param {Object} object The object to inspect. + * @param {String} property The property to get the result of. + * @returns {Mixed} Returns the resolved value. + * @example + * + * var object = { + * 'cheese': 'crumpets', + * 'stuff': function() { + * return 'nonsense'; + * } + * }; + * + * _.result(object, 'cheese'); + * // => 'crumpets' + * + * _.result(object, 'stuff'); + * // => 'nonsense' + */ + function result(object, property) { + // based on Backbone's private `getValue` function + // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 + if (!object) { + return null; + } + var value = object[property]; + return toString.call(value) == funcClass ? object[property]() : value; + } + + /** + * A micro-templating method that handles arbitrary delimiters, preserves + * whitespace, and correctly escapes quotes within interpolated code. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} text The template text. + * @param {Obect} data The data object used to populate the text. + * @param {Object} options The options object. + * @returns {Function|String} Returns a compiled function when no `data` object + * is given, else it returns the interpolated text. + * @example + * + * // using compiled template + * var compiled = _.template('hello: <%= name %>'); + * compiled({ 'name': 'moe' }); + * // => 'hello: moe' + * + * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; + * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); + * // => '
  • moe
  • curly
  • larry
  • ' + * + * var template = _.template('<%- value %>'); + * template({ 'value': ' + */ + function template(text, data, options) { + // based on John Resig's `tmpl` implementation + // http://ejohn.org/blog/javascript-micro-templating/ + // and Laura Doktorova's doT.js + // https://github.com/olado/doT + options || (options = {}); + + var isEvaluating, + result, + escapeDelimiter = options.escape, + evaluateDelimiter = options.evaluate, + interpolateDelimiter = options.interpolate, + settings = lodash.templateSettings, + variable = options.variable; + + // use default settings if no options object is provided + if (escapeDelimiter == null) { + escapeDelimiter = settings.escape; + } + if (evaluateDelimiter == null) { + evaluateDelimiter = settings.evaluate; + } + if (interpolateDelimiter == null) { + interpolateDelimiter = settings.interpolate; + } + + // tokenize delimiters to avoid escaping them + if (escapeDelimiter) { + text = text.replace(escapeDelimiter, tokenizeEscape); + } + if (interpolateDelimiter) { + text = text.replace(interpolateDelimiter, tokenizeInterpolate); + } + if (evaluateDelimiter != lastEvaluateDelimiter) { + // generate `reEvaluateDelimiter` to match `_.templateSettings.evaluate` + // and internal ``, `` delimiters + lastEvaluateDelimiter = evaluateDelimiter; + reEvaluateDelimiter = RegExp( + (evaluateDelimiter ? evaluateDelimiter.source : '($^)') + + '||' + , 'g'); + } + isEvaluating = tokenized.length; + text = text.replace(reEvaluateDelimiter, tokenizeEvaluate); + isEvaluating = isEvaluating != tokenized.length; + + // escape characters that cannot be included in string literals and + // detokenize delimiter code snippets + text = "__p += '" + text + .replace(reUnescapedString, escapeStringChar) + .replace(reToken, detokenize) + "';\n"; + + // clear stored code snippets + tokenized.length = 0; + + // if `options.variable` is not specified and the template contains "evaluate" + // delimiters, wrap a with-statement around the generated code to add the + // data object to the top of the scope chain + if (!variable) { + variable = settings.variable || lastVariable || 'obj'; + + if (isEvaluating) { + text = 'with (' + variable + ') {\n' + text + '\n}\n'; + } + else { + if (variable != lastVariable) { + // generate `reDoubleVariable` to match references like `obj.obj` inside + // transformed "escape" and "interpolate" delimiters + lastVariable = variable; + reDoubleVariable = RegExp('(\\(\\s*)' + variable + '\\.' + variable + '\\b', 'g'); + } + // avoid a with-statement by prepending data object references to property names + text = text + .replace(reInsertVariable, '$&' + variable + '.') + .replace(reDoubleVariable, '$1__d'); + } + } + + // cleanup code by stripping empty strings + text = ( isEvaluating ? text.replace(reEmptyStringLeading, '') : text) + .replace(reEmptyStringMiddle, '$1') + .replace(reEmptyStringTrailing, '$1;'); + + // frame code as the function body + text = 'function(' + variable + ') {\n' + + variable + ' || (' + variable + ' = {});\n' + + 'var __t, __p = \'\', __e = _.escape' + + (isEvaluating + ? ', __j = Array.prototype.join;\n' + + 'function print() { __p += __j.call(arguments, \'\') }\n' + : ', __d = ' + variable + '.' + variable + ' || ' + variable + ';\n' + ) + + text + + 'return __p\n}'; + + // add a sourceURL for easier debugging + // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl + if (useSourceURL) { + text += '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']'; + } + + try { + result = Function('_', 'return ' + text)(lodash); + } catch(e) { + result = function() { throw e; }; + } + + if (data) { + return result(data); + } + // provide the compiled function's source via its `toString` method, in + // supported environments, or the `source` property as a convenience for + // build time precompilation + result.source = text; + return result; + } + + /** + * Executes the `callback` function `n` times. The `callback` is bound to + * `thisArg` and invoked with 1 argument; (index). + * + * @static + * @memberOf _ + * @category Utilities + * @param {Number} n The number of times to execute the callback. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @example + * + * _.times(3, function() { genie.grantWish(); }); + * // => calls `genie.grantWish()` 3 times + * + * _.times(3, function() { this.grantWish(); }, genie); + * // => also calls `genie.grantWish()` 3 times + */ + function times(n, callback, thisArg) { + var index = -1; + if (thisArg) { + while (++index < n) { + callback.call(thisArg, index); + } + } else { + while (++index < n) { + callback(index); + } + } + } + + /** + * Generates a unique id. If `prefix` is passed, the id will be appended to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {String} [prefix] The value to prefix the id with. + * @returns {Number|String} Returns a numeric id if no prefix is passed, else + * a string id may be returned. + * @example + * + * _.uniqueId('contact_'); + * // => 'contact_104' + */ + function uniqueId(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Wraps the value in a `lodash` wrapper object. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to wrap. + * @returns {Object} Returns the wrapper object. + * @example + * + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * var youngest = _.chain(stooges) + * .sortBy(function(stooge) { return stooge.age; }) + * .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) + * .first() + * .value(); + * // => 'moe is 40' + */ + function chain(value) { + value = new LoDash(value); + value._chain = true; + return value; + } + + /** + * Invokes `interceptor` with the `value` as the first argument, and then + * returns `value`. The purpose of this method is to "tap into" a method chain, + * in order to perform operations on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chaining + * @param {Mixed} value The value to pass to `callback`. + * @param {Function} interceptor The function to invoke. + * @returns {Mixed} Returns `value`. + * @example + * + * _.chain([1,2,3,200]) + * .filter(function(num) { return num % 2 == 0; }) + * .tap(alert) + * .map(function(num) { return num * num }) + * .value(); + * // => // [2, 200] (alerted) + * // => [4, 40000] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * Enables method chaining on the wrapper object. + * + * @name chain + * @deprecated + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapper object. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperChain() { + this._chain = true; + return this; + } + + /** + * Extracts the wrapped value. + * + * @name value + * @memberOf _ + * @category Chaining + * @returns {Mixed} Returns the wrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return this._wrapped; + } + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type String + */ + lodash.VERSION = '0.4.1'; + + // assign static methods + lodash.after = after; + lodash.bind = bind; + lodash.bindAll = bindAll; + lodash.chain = chain; + lodash.clone = clone; + lodash.compact = compact; + lodash.compose = compose; + lodash.contains = contains; + lodash.debounce = debounce; + lodash.defaults = defaults; + lodash.defer = defer; + lodash.delay = delay; + lodash.difference = difference; + lodash.escape = escape; + lodash.every = every; + lodash.extend = extend; + lodash.filter = filter; + lodash.find = find; + lodash.first = first; + lodash.flatten = flatten; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.functions = functions; + lodash.groupBy = groupBy; + lodash.has = has; + lodash.identity = identity; + lodash.indexOf = indexOf; + lodash.initial = initial; + lodash.intersection = intersection; + lodash.invoke = invoke; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isBoolean = isBoolean; + lodash.isDate = isDate; + lodash.isElement = isElement; + lodash.isEmpty = isEmpty; + lodash.isEqual = isEqual; + lodash.isFinite = isFinite; + lodash.isFunction = isFunction; + lodash.isNaN = isNaN; + lodash.isNull = isNull; + lodash.isNumber = isNumber; + lodash.isObject = isObject; + lodash.isRegExp = isRegExp; + lodash.isString = isString; + lodash.isUndefined = isUndefined; + lodash.keys = keys; + lodash.last = last; + lodash.lastIndexOf = lastIndexOf; + lodash.map = map; + lodash.max = max; + lodash.memoize = memoize; + lodash.min = min; + lodash.mixin = mixin; + lodash.noConflict = noConflict; + lodash.once = once; + lodash.partial = partial; + lodash.pick = pick; + lodash.pluck = pluck; + lodash.range = range; + lodash.reduce = reduce; + lodash.reduceRight = reduceRight; + lodash.reject = reject; + lodash.rest = rest; + lodash.result = result; + lodash.shuffle = shuffle; + lodash.size = size; + lodash.some = some; + lodash.sortBy = sortBy; + lodash.sortedIndex = sortedIndex; + lodash.tap = tap; + lodash.template = template; + lodash.throttle = throttle; + lodash.times = times; + lodash.toArray = toArray; + lodash.union = union; + lodash.uniq = uniq; + lodash.uniqueId = uniqueId; + lodash.values = values; + lodash.without = without; + lodash.wrap = wrap; + lodash.zip = zip; + lodash.zipObject = zipObject; + + // assign aliases + lodash.all = every; + lodash.any = some; + lodash.collect = map; + lodash.detect = find; + lodash.each = forEach; + lodash.foldl = reduce; + lodash.foldr = reduceRight; + lodash.head = first; + lodash.include = contains; + lodash.inject = reduce; + lodash.methods = functions; + lodash.select = filter; + lodash.tail = rest; + lodash.take = first; + lodash.unique = uniq; + + // add pseudo private properties used and removed during the build process + lodash._iteratorTemplate = iteratorTemplate; + lodash._shimKeys = shimKeys; + + /*--------------------------------------------------------------------------*/ + + // assign private `LoDash` constructor's prototype + LoDash.prototype = lodash.prototype; + + // add all static functions to `LoDash.prototype` + mixin(lodash); + + // add `LoDash.prototype.chain` after calling `mixin()` to avoid overwriting + // it with the wrapped `lodash.chain` + LoDash.prototype.chain = wrapperChain; + LoDash.prototype.value = wrapperValue; + + // add all mutator Array functions to the wrapper. + forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped; + func.apply(value, arguments); + + // IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` + // functions that fail to remove the last element, `value[0]`, of + // array-like objects even though the `length` property is set to `0`. + // The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + // is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + if (value.length === 0) { + delete value[0]; + } + if (this._chain) { + value = new LoDash(value); + value._chain = true; + } + return value; + }; + }); + + // add all accessor Array functions to the wrapper. + forEach(['concat', 'join', 'slice'], function(methodName) { + var func = ArrayProto[methodName]; + + LoDash.prototype[methodName] = function() { + var value = this._wrapped, + result = func.apply(value, arguments); + + if (this._chain) { + result = new LoDash(result); + result._chain = true; + } + return result; + }; + }); + + /*--------------------------------------------------------------------------*/ + + // expose Lo-Dash + // some AMD build optimizers, like r.js, check for specific condition patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Expose Lo-Dash to the global object even when an AMD loader is present in + // case Lo-Dash was injected by a third-party script and not intended to be + // loaded as a module. The global assignment can be reverted in the Lo-Dash + // module via its `noConflict()` method. + window._ = lodash; + + // define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module + define('matrix/../../lib/lodash',[],function() { + return lodash; + }); + } + // check for `exports` after `define` in case a build optimizer adds an `exports` object + else if (freeExports) { + // in Node.js or RingoJS v0.8.0+ + if (typeof module == 'object' && module && module.exports == freeExports) { + (module.exports = lodash)._ = lodash; + } + // in Narwhal or RingoJS v0.7.0- + else { + freeExports._ = lodash; + } + } + else { + // in a browser or Rhino + window._ = lodash; + } +}(this)); + +define('matrix/matrix2-api',['require','common/not-implemented','matrix/m2'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M2(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + + return result; + } + + function clear( m ) { + m[0] = m[1] = 0; + m[2] = m[3] = 0; + + return m; + } + + function determinant( m ) { + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3]; + + return a00 * a11 - a01 * a10; + } + + function equal( m1, m2, e ) { + e = e || 0.000001; + + if( m1.length !== m2.length ) { + return false; + } + + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e ) { + return false; + } + + return true; + } + + function inverse( m, result ) { + result = result || new M2(); + + var a00 = m[0], a01 = m[1], + a10 = m[2], a11 = m[3], + + determinant = a00 * a11 - a01 * a10, + inverseDeterminant; + + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; + + result[0] = a11 * inverseDeterminant; + result[1] = -a01 * inverseDeterminant; + result[2] = -a10 * inverseDeterminant; + result[3] = a00 * inverseDeterminant; + + return result; + } + + function multiply( m1, m2, result ) { + result = result || new M2(); + + var a00 = m1[0], a01 = m1[1], a10 = m1[2], a11 = m1[3]; + var b00 = m2[0], b01 = m2[1], b10 = m2[2], b11 = m2[3]; + + result[0] = a00 * b00 + a01 * b10; + result[1] = a00 * b01 + a01 * b11; + result[2] = a10 * b00 + a11 * b10; + result[3] = a10 * b01 + a11 * b11; + + return result; + } + + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + } + + return m; + } + + function subtract( m1, m2, result ) { + result = result || new M2(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + + return result; + } + + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1]; + + result[1] = m[2]; + result[2] = a01; + + return result; + } + + result = result || new M2(); + + result[0] = m[0]; + result[1] = m[2]; + result[2] = m[1]; + result[3] = m[3]; + + return result; + } + + var matrix2 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M2( 0, 0, + 0, 0 ), + one: new M2( 1, 1, + 1, 1 ), + identity: new M2( 1, 0, + 0, 1 ) + }; + + return matrix2; + + }; + +}); +define('matrix/matrix2',['require','../../lib/lodash','common/not-implemented','matrix/m2','matrix/matrix2-api','matrix/matrix'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M2 = require( "matrix/m2" )( FLOAT_ARRAY_TYPE ); + var matrix2 = require( "matrix/matrix2-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } + + function updateViews() { + var i; + for( i = 0; i < 2; ++ i ) { + this._views[i] = new Matrix2View( this, this.buffer, + i*2, (i+1)*2 ); + } + } + + var Matrix2View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); + + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + } + }); + }; + + var Matrix2 = function( arg1, arg2, + arg3, arg4 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + this.buffer = new M2( arg1.buffer ); + } else { + this.buffer = new M2( arg1 ); + } + } else if( 4 === argc ) { + this.buffer = new M2( arg1, arg2, + arg3, arg4 ); + } else { + this.buffer = new M2(); + } + + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + } + }); + + this._views = []; + + updateViews.call( this ); + + this.modified = true; + }; + Matrix2.prototype = new Matrix(); + Matrix2.prototype.constructor = Matrix2; + + function add( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix2.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function clear() { + matrix2.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Matrix2( this ); + } + + function determinant() { + return matrix2.determinant( this.buffer ); + } + + function equal( arg ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix2.equal( this.buffer, other ); + } + + function inverse( result ) { + result = result || this; + if( !matrix2.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix2.inverse( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix2.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix2 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix2 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix2.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transpose( result ) { + result = result || this; + matrix2.transpose( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Matrix2.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix2; + + }; + +}); +define('matrix/m3',['require','matrix/m'],function ( require ) { + + var M = require( "matrix/m" ); + + return function( FLOAT_ARRAY_TYPE ) { + + var M3 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, + 0, 0, 0, + 0, 0, 0]; + } else { + elements = arguments; + } + + var matrix = new FLOAT_ARRAY_TYPE( 9 ); + for( var i = 0; i < 9; ++ i ) { + matrix[i] = elements[i]; + } + + return matrix; + }; + M3.prototype = new M(); + M3.prototype.constructor = M3; + + return M3; + + }; + +}); +define('matrix/matrix3-api',['require','common/not-implemented','matrix/m3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M3(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; + + return result; + } + + function clear( m ) { + m[0] = m[1] = m[2] = 0; + m[3] = m[4] = m[5] = 0; + m[6] = m[7] = m[8] = 0; + + return m; + } + + function determinant( m ) { + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + } + + function equal( m1, m2, e ) { + e = e || 0.000001; + + if( m1.length !== m2.length ) { + return false; + } + + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e ) { + return false; + } + + return true; + } + + function inverse( m, result ) { + result = result || new M3(); + + var a00 = m[0], a01 = m[1], a02 = m[2], + a10 = m[3], a11 = m[4], a12 = m[5], + a20 = m[6], a21 = m[7], a22 = m[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + determinant = a00 * b01 + a01 * b11 + a02 * b21, + inverseDeterminant; + + if( !determinant ) { return null; } + inverseDeterminant = 1 / determinant; + + result[0] = b01 * inverseDeterminant; + result[1] = (-a22 * a01 + a02 * a21) * inverseDeterminant; + result[2] = (a12 * a01 - a02 * a11) * inverseDeterminant; + result[3] = b11 * inverseDeterminant; + result[4] = (a22 * a00 - a02 * a20) * inverseDeterminant; + result[5] = (-a12 * a00 + a02 * a10) * inverseDeterminant; + result[6] = b21 * inverseDeterminant; + result[7] = (-a21 * a00 + a01 * a20) * inverseDeterminant; + result[8] = (a11 * a00 - a01 * a10) * inverseDeterminant; + + return result; + } + + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L682 + function multiply( m1, m2, result ) { + result = result || new M3(); - var _matrix2 = matrix2( _FLOAT_ARRAY_TYPE ); - var _matrix3 = matrix3( _FLOAT_ARRAY_TYPE ); - var _matrix4 = matrix4( _FLOAT_ARRAY_TYPE ); - var _transform = transform( _FLOAT_ARRAY_TYPE ); + // Cache the matrix values (makes for huge speed increases!) + var a00 = m1[0], a01 = m1[1], a02 = m1[2], + a10 = m1[3], a11 = m1[4], a12 = m1[5], + a20 = m1[6], a21 = m1[7], a22 = m1[8], + + b00 = m2[0], b01 = m2[1], b02 = m2[2], + b10 = m2[3], b11 = m2[4], b12 = m2[5], + b20 = m2[6], b21 = m2[7], b22 = m2[8]; + + result[0] = a00 * b00 + a01 * b10 + a02 * b20; + result[1] = a00 * b01 + a01 * b11 + a02 * b21; + result[2] = a00 * b02 + a01 * b12 + a02 * b22; + + result[3] = a10 * b00 + a11 * b10 + a12 * b20; + result[4] = a10 * b01 + a11 * b11 + a12 * b21; + result[5] = a10 * b02 + a11 * b12 + a12 * b22; + + result[6] = a20 * b00 + a21 * b10 + a22 * b20; + result[7] = a20 * b01 + a21 * b11 + a22 * b21; + result[8] = a20 * b02 + a21 * b12 + a22 * a22; + + return result; + } + + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + } + + return m; + } + + function subtract( m1, m2, result ) { + result = result || new M3(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; + + return result; + } + + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], + a12 = m[5]; + + result[1] = m[3]; + result[2] = m[6]; + result[3] = a01; + result[5] = m[7]; + result[6] = a02; + result[7] = a12; + + return result; + } + + result = result || new M3(); + + result[0] = m[0]; + result[1] = m[3]; + result[2] = m[6]; + result[3] = m[1]; + result[4] = m[4]; + result[5] = m[7]; + result[6] = m[2]; + result[7] = m[5]; + result[8] = m[8]; + + return result; + } + + var matrix3 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M3( 0, 0, 0, + 0, 0, 0, + 0, 0, 0 ), + one: new M3( 1, 1, 1, + 1, 1, 1, + 1, 1, 1 ), + identity: new M3( 1, 0, 0, + 0, 1, 0, + 0, 0, 1 ) + }; + + return matrix3; + + }; + +}); +define('matrix/matrix3',['require','../../lib/lodash','common/not-implemented','matrix/m3','matrix/matrix3-api','matrix/matrix'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); + var matrix3 = require( "matrix/matrix3-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } + + function updateViews() { + var i; + for( i = 0; i < 3; ++ i ) { + this._views[i] = new Matrix3View( this, this.buffer, + i*3, (i+1)*3 ); + } + } + + var Matrix3View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); + + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + } + }); + }; + + var Matrix3 = function( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + this.buffer = new M3( arg1.buffer ); + } else { + this.buffer = new M3( arg1 ); + } + } else if( 9 === argc ) { + this.buffer = new M3( arg1, arg2, arg3, + arg4, arg5, arg6, + arg7, arg8, arg9 ); + } else { + this.buffer = new M3(); + } + + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + } + }); + + this._views = []; + + updateViews.call( this ); + + this.modified = true; + }; + Matrix3.prototype = new Matrix(); + Matrix3.prototype.constructor = Matrix3; + + function add( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix3.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function clear() { + matrix3.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Matrix3( this ); + } + + function determinant() { + return matrix3.determinant( this.buffer ); + } + + function equal( arg ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix3.equal( this.buffer, other ); + } + + function inverse( result ) { + result = result || this; + if( !matrix3.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix3.inverse( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix3.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix3 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix3.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transpose( result ) { + result = result || this; + matrix3.transpose( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Matrix3.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix3; + + }; + +}); +define('matrix/m4',['require','matrix/m'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var M = require( "matrix/m" ); + + var M4 = function() { + var elements = null; + var argc = arguments.length; + if( 1 === argc) { + elements = arguments[0]; + } else if( 0 === argc ) { + elements = [0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0]; + } else { + elements = arguments; + } + + var matrix = new FLOAT_ARRAY_TYPE( 16 ); + for( var i = 0; i < 16; ++ i ) { + matrix[i] = elements[i]; + } + + return matrix; + }; + M4.prototype = new M(); + M4.prototype.constructor = M4; + + return M4; + + }; + +}); +define('matrix/matrix4-api',['require','common/not-implemented','matrix/m4','vector/v3'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var V3 = require( "vector/v3" )( FLOAT_ARRAY_TYPE ); + + function add( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] + m2[0]; + result[1] = m1[1] + m2[1]; + result[2] = m1[2] + m2[2]; + result[3] = m1[3] + m2[3]; + result[4] = m1[4] + m2[4]; + result[5] = m1[5] + m2[5]; + result[6] = m1[6] + m2[6]; + result[7] = m1[7] + m2[7]; + result[8] = m1[8] + m2[8]; + result[9] = m1[9] + m2[9]; + result[10] = m1[10] + m2[10]; + result[11] = m1[11] + m2[11]; + result[12] = m1[12] + m2[12]; + result[13] = m1[13] + m2[13]; + result[14] = m1[14] + m2[14]; + result[15] = m1[15] + m2[15]; + + return result; + } + + function clear( m ) { + m[0] = m[1] = m[2] = m[3] = 0; + m[4] = m[5] = m[6] = m[7] = 0; + m[8] = m[9] = m[10] = m[11] = 0; + m[12] = m[13] = m[14] = m[15] = 0; + + return m; + } + + function determinant( m ) { + var a0 = m[0] * m[5] - m[1] * m[4]; + var a1 = m[0] * m[6] - m[2] * m[4]; + var a2 = m[0] * m[7] - m[3] * m[4]; + var a3 = m[1] * m[6] - m[2] * m[5]; + var a4 = m[1] * m[7] - m[3] * m[5]; + var a5 = m[2] * m[7] - m[3] * m[6]; + var b0 = m[8] * m[13] - m[9] * m[12]; + var b1 = m[8] * m[14] - m[10] * m[12]; + var b2 = m[8] * m[15] - m[11] * m[12]; + var b3 = m[9] * m[14] - m[10] * m[13]; + var b4 = m[9] * m[15] - m[11] * m[13]; + var b5 = m[10] * m[15] - m[11] * m[14]; + + return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; + } + + function equal( m1, m2, e ) { + e = e || 0.000001; + + if( m1.length !== m2.length ) { + return false; + } + + var d0 = Math.abs( m1[0] - m2[0] ); + var d1 = Math.abs( m1[1] - m2[1] ); + var d2 = Math.abs( m1[2] - m2[2] ); + var d3 = Math.abs( m1[3] - m2[3] ); + var d4 = Math.abs( m1[4] - m2[4] ); + var d5 = Math.abs( m1[5] - m2[5] ); + var d6 = Math.abs( m1[6] - m2[6] ); + var d7 = Math.abs( m1[7] - m2[7] ); + var d8 = Math.abs( m1[8] - m2[8] ); + var d9 = Math.abs( m1[9] - m2[9] ); + var d10 = Math.abs( m1[10] - m2[10] ); + var d11 = Math.abs( m1[11] - m2[11] ); + var d12 = Math.abs( m1[12] - m2[12] ); + var d13 = Math.abs( m1[13] - m2[13] ); + var d14 = Math.abs( m1[14] - m2[14] ); + var d15 = Math.abs( m1[15] - m2[15] ); + + if( isNaN( d0 ) || d0 > e || + isNaN( d1 ) || d1 > e || + isNaN( d2 ) || d2 > e || + isNaN( d3 ) || d3 > e || + isNaN( d4 ) || d4 > e || + isNaN( d5 ) || d5 > e || + isNaN( d6 ) || d6 > e || + isNaN( d7 ) || d7 > e || + isNaN( d8 ) || d8 > e || + isNaN( d9 ) || d9 > e || + isNaN( d10 ) || d10 > e || + isNaN( d11 ) || d11 > e || + isNaN( d12 ) || d12 > e || + isNaN( d13 ) || d13 > e || + isNaN( d14 ) || d14 > e || + isNaN( d15 ) || d15 > e ) { + return false; + } + + return true; + } + + function inverse( m, result ) { + result = result || new M4(); + + var a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3], + a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7], + a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11], + a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + determinant = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), + inverseDeterminant; + + // Determinant, throw exception if singular + if( !determinant ) { + return undefined; + } + + inverseDeterminant = 1 / determinant; + + result[0] = (a11 * b11 - a12 * b10 + a13 * b09) * inverseDeterminant; + result[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * inverseDeterminant; + result[2] = (a31 * b05 - a32 * b04 + a33 * b03) * inverseDeterminant; + result[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * inverseDeterminant; + result[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * inverseDeterminant; + result[5] = (a00 * b11 - a02 * b08 + a03 * b07) * inverseDeterminant; + result[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * inverseDeterminant; + result[7] = (a20 * b05 - a22 * b02 + a23 * b01) * inverseDeterminant; + result[8] = (a10 * b10 - a11 * b08 + a13 * b06) * inverseDeterminant; + result[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * inverseDeterminant; + result[10] = (a30 * b04 - a31 * b02 + a33 * b00) * inverseDeterminant; + result[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * inverseDeterminant; + result[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * inverseDeterminant; + result[13] = (a00 * b09 - a01 * b07 + a02 * b06) * inverseDeterminant; + result[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * inverseDeterminant; + result[15] = (a20 * b03 - a21 * b01 + a22 * b00) * inverseDeterminant; + + return result; + } + + // https://github.com/toji/gl-matrix/blob/8d6179c15aa938159feb2cb617d8a3af3fa2c7f3/gl-matrix.js#L1295 + function multiply( m1, m2, result ) { + result = result || new M4(); + + var a00 = m1[0], a01 = m1[1], a02 = m1[2], a03 = m1[3], + a10 = m1[4], a11 = m1[5], a12 = m1[6], a13 = m1[7], + a20 = m1[8], a21 = m1[9], a22 = m1[10], a23 = m1[11], + a30 = m1[12], a31 = m1[13], a32 = m1[14], a33 = m1[15], + + b00 = m2[0], b01 = m2[1], b02 = m2[2], b03 = m2[3], + b10 = m2[4], b11 = m2[5], b12 = m2[6], b13 = m2[7], + b20 = m2[8], b21 = m2[9], b22 = m2[10], b23 = m2[11], + b30 = m2[12], b31 = m2[13], b32 = m2[14], b33 = m2[15]; + + result[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; + result[1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; + result[2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; + result[3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; + result[4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; + result[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; + result[6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; + result[7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; + result[8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; + result[9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; + result[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; + result[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; + result[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; + result[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; + result[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; + result[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; + + return result; + } + + function multiplyV3( m, v, result ) { + result = result || new V3(); + + var x = v[0], y = v[1], z = v[2]; + + result[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + result[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + result[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + + return result; + } + + function set( m ) { + if( 2 === arguments.length ) { + var values = arguments[1]; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + m[9] = values[9]; + m[10] = values[10]; + m[11] = values[11]; + m[12] = values[12]; + m[13] = values[13]; + m[14] = values[14]; + m[15] = values[15]; + } else { + m[0] = arguments[1]; + m[1] = arguments[2]; + m[2] = arguments[3]; + m[3] = arguments[4]; + m[4] = arguments[5]; + m[5] = arguments[6]; + m[6] = arguments[7]; + m[7] = arguments[8]; + m[8] = arguments[9]; + m[9] = arguments[10]; + m[10] = arguments[11]; + m[11] = arguments[12]; + m[12] = arguments[13]; + m[13] = arguments[14]; + m[14] = arguments[15]; + m[15] = arguments[16]; + } + + return m; + } + + function subtract( m1, m2, result ) { + result = result || new M4(); + + result[0] = m1[0] - m2[0]; + result[1] = m1[1] - m2[1]; + result[2] = m1[2] - m2[2]; + result[3] = m1[3] - m2[3]; + result[4] = m1[4] - m2[4]; + result[5] = m1[5] - m2[5]; + result[6] = m1[6] - m2[6]; + result[7] = m1[7] - m2[7]; + result[8] = m1[8] - m2[8]; + result[9] = m1[9] - m2[9]; + result[10] = m1[10] - m2[10]; + result[11] = m1[11] - m2[11]; + result[12] = m1[12] - m2[12]; + result[13] = m1[13] - m2[13]; + result[14] = m1[14] - m2[14]; + result[15] = m1[15] - m2[15]; + + return result; + } + + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], a03 = m[3], + a12 = m[6], a13 = m[7], + a23 = m[11]; + + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = a01; + result[6] = m[9]; + result[7] = m[13]; + result[8] = a02; + result[9] = a12; + result[11] = m[14]; + result[12] = a03; + result[13] = a13; + result[14] = a23; + + return result; + } + + result = result || new M4(); + + result[0] = m[0]; + result[1] = m[4]; + result[2] = m[8]; + result[3] = m[12]; + result[4] = m[1]; + result[5] = m[5]; + result[6] = m[9]; + result[7] = m[13]; + result[8] = m[2]; + result[9] = m[6]; + result[10] = m[10]; + result[11] = m[14]; + result[12] = m[3]; + result[13] = m[7]; + result[14] = m[11]; + result[15] = m[15]; + + return result; + } + + var matrix4 = { + add: add, + clear: clear, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + multiplyV3: notImplemented, + set: set, + subtract: subtract, + transpose: transpose, + + zero: new M4( 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 ), + one: new M4( 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1 ), + identity: new M4( 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ) + }; + + return matrix4; + + }; + +}); +define('matrix/matrix4',['require','../../lib/lodash','common/not-implemented','matrix/m4','matrix/matrix4-api','matrix/matrix'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var _ = require( "../../lib/lodash" ); + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix = require( "matrix/matrix" ); + + function getView( index ) { + return this._views[index]; + } + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } + + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new Matrix4View( this, this.buffer, + i*4, (i+1)*4 ); + } + } + + var Matrix4View = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); + + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + }; + + var Matrix4 = function( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else { + this.buffer = new M4( arg1 ); + } + } else if( 16 === argc ) { + this.buffer = new M4( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ); + } else { + this.buffer = new M4(); + } + + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); + + this._views = []; + + updateViews.call( this ); + + this.modified = true; + }; + Matrix4.prototype = new Matrix(); + Matrix4.prototype.constructor = Matrix4; + + function add( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.add( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function clear() { + matrix4.clear( this.buffer ); + this.modified = true; + + return this; + } + + function clone() { + return new Matrix4( this ); + } + + function determinant() { + return matrix4.determinant( this.buffer ); + } + + function equal( arg ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix4.equal( this.buffer, other ); + } + + function inverse( result ) { + result = result || this; + if( !matrix4.determinant( this.buffer ) ) { + throw new Error( "matrix is singular" ); + } + matrix4.inverse( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function set( arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, + arg9, arg10, arg11, arg12, + arg13, arg14, arg15, arg16 ) { + var argc = arguments.length; + var buffer = this.buffer; + var other; + if( 1 === argc ) { + if( arg1 instanceof Matrix4 ) { + other = arg1.buffer; + } else { + other = arg1; + } + buffer[0] = other[0]; + buffer[1] = other[1]; + buffer[2] = other[2]; + buffer[3] = other[3]; + buffer[4] = other[4]; + buffer[5] = other[5]; + buffer[6] = other[6]; + buffer[7] = other[7]; + buffer[8] = other[8]; + buffer[9] = other[9]; + buffer[10] = other[10]; + buffer[11] = other[11]; + buffer[12] = other[12]; + buffer[13] = other[13]; + buffer[14] = other[14]; + buffer[15] = other[15]; + this.modified = true; + } else if( 16 === argc ) { + buffer[0] = arg1; + buffer[1] = arg2; + buffer[2] = arg3; + buffer[3] = arg4; + buffer[4] = arg5; + buffer[5] = arg6; + buffer[6] = arg7; + buffer[7] = arg8; + buffer[8] = arg9; + buffer[9] = arg10; + buffer[10] = arg11; + buffer[11] = arg12; + buffer[12] = arg13; + buffer[13] = arg14; + buffer[14] = arg15; + buffer[15] = arg16; + this.modified = true; + } + + return this; + } + + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.subtract( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function transpose( result ) { + result = result || this; + matrix4.transpose( this.buffer, result.buffer ); + result.modified = true; + + return this; + } + + _.extend( Matrix4.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); + + return Matrix4; + + }; + +}); +define('matrix/transform-api',['require','common/not-implemented','matrix/m4','matrix/matrix4-api'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + + function compound( transform, t, r, s ) { + + if( t ) { + translate( t, transform ); + } + + if( r ) { + rotate( r, transform ); + } + + if( s ) { + scale( s, transform ); + } + + return transform; + } + + function set(transform, t, r, s){ + if (transform){ + matrix4.set(transform, matrix4.identity); + } + return compound(transform, t, r, s); + } + + function rotate( v, result ) { + result = result || new M4( matrix4.identity ); + + var sinA, + cosA; + var rotation; + + if( 0 !== v[2] ) { + sinA = Math.sin( v[2] ); + cosA = Math.cos( v[2] ); + + rotation = [ cosA, -sinA, 0, 0, + sinA, cosA, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } + + if( 0 !== v[1] ) { + sinA = Math.sin( v[1] ); + cosA = Math.cos( v[1] ); + + rotation = [ cosA, 0, sinA, 0, + 0, 1, 0, 0, + -sinA, 0, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } + + if( 0 !== v[0] ) { + sinA = Math.sin( v[0] ); + cosA = Math.cos( v[0] ); - Object.defineProperty( this, 'Vector2', { - get: function() { - return _vector2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector2', { - get: function() { - return _vector2; - }, - enumerable: true - }); + rotation = [ 1, 0, 0, 0, + 0, cosA, -sinA, 0, + 0, sinA, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } + + return result; + } + + function scale( v, result ) { + result = result || new M4( matrix4.identity ); + + matrix4.multiply( result, [v[0], 0, 0, 0, + 0, v[1], 0, 0, + 0, 0, v[2], 0, + 0, 0, 0, 1], result ); + + return result; + } + + function translate( v, result ) { + result = result || new M4( matrix4.identity ); + + matrix4.multiply( result, [1, 0, 0, v[0], + 0, 1, 0, v[1], + 0, 0, 1, v[2], + 0, 0, 0, 1], result ); + + return result; + } + + var transform = { + compound: compound, + set: set, + rotate: rotate, + scale: scale, + translate: translate + }; + + return transform; + + }; + +}); +define('matrix/t',['require','matrix/m','matrix/m4','matrix/transform-api'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var M = require( "matrix/m" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require("matrix/transform-api")( FLOAT_ARRAY_TYPE ); + + var T = function(t, r, s) { + var matrix = new M4(); + return transform.set(matrix, t, r, s); + }; + T.prototype = new M(); + T.prototype.constructor = T; + + return T; + + }; + +}); +define('matrix/transform',['require','common/not-implemented','matrix/m4','matrix/transform-api','matrix/matrix4-api','matrix/matrix4'],function ( require ) { + + return function( FLOAT_ARRAY_TYPE ) { + + var notImplemented = require( "common/not-implemented" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require( "matrix/transform-api" )( FLOAT_ARRAY_TYPE ); + var matrix4 = require( "matrix/matrix4-api" )( FLOAT_ARRAY_TYPE ); + var Matrix4 = require( "matrix/matrix4" )( FLOAT_ARRAY_TYPE ); + + function getView( index ) { + return this._views[index]; + } + + function getValue( index ) { + return this.buffer[index]; + } + + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } + + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new TransformView( this, this.buffer, + i*4, (i+1)*4 ); + } + } + + var TransformView = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); + + Object.defineProperties( this, { + "0": { + get: getValue.bind( this, 0 ), + set: setValue.bind( this, 0 ) + }, + "1": { + get: getValue.bind( this, 1 ), + set: setValue.bind( this, 1 ) + }, + "2": { + get: getValue.bind( this, 2 ), + set: setValue.bind( this, 2 ) + }, + "3": { + get: getValue.bind( this, 3 ), + set: setValue.bind( this, 3 ) + } + }); + }; + + var Transform = function( arg1, arg2, arg3 ) { + var argc = arguments.length; + if( 1 === argc ) { + if( arg1 instanceof Transform || + arg1 instanceof Matrix4 ) { + this.buffer = new M4( arg1.buffer ); + } else if( arg1 instanceof M4 ) { + this.buffer = new M4( arg1 ); + } else { + this.buffer = transform.compound( arg1, arg2, arg3 ); + } + } else { + this.buffer = transform.compound( arg1, arg2, arg3 ); + } + + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + }, + "3": { + get: getView.bind( this, 3 ) + } + }); + + this._views = []; + + updateViews.call( this ); + + this.modified = true; + }; + + function clone() { + return new Transform( this ); + } + + function equal( arg ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } + + return matrix4.equal( this.buffer, other ); + } + + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } + + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; + + return this; + } + + function rotate( v, result ) { + var rotation = transform.rotate( v ); + + result = result || this; + matrix4.multiply( this.buffer, rotation, result.buffer ); + result.modified = true; + + return this; + } + + function scale( v, result ) { + var scaled = transform.scale( v ); + + result = result || this; + matrix4.multiply( this.buffer, scaled, result.buffer ); + result.modified = true; + + return this; + } + + function set( t, r, s ) { + transform.compound( this.buffer, t, r, s ); + this.modified = true; + } + + function transformDirection( v, result ) { + + } + + function transformPoint( v, result ) { + + } + + function translate( v, result ) { + var translation = transform.translate( v ); + + result = result || this; + matrix4.multiply( this.buffer, translation, result.buffer ); + result.modified = true; + + return this; + } + + Transform.prototype = { + clone: clone, + equal: equal, + inverseTransformDirection: notImplemented, + inverseTransformPoint: notImplemented, + multiply: multiply, + rotate: rotate, + scale: scale, + set: set, + transformDirection: notImplemented, + transformPoint: notImplemented, + translate: translate + }; + + return Transform; + + }; + +}); +define('_math',['require','constants','equal','vector/v2','vector/vector2','vector/vector2-api','vector/v3','vector/vector3','vector/vector3-api','vector/v4','vector/vector4','vector/vector4-api','matrix/m2','matrix/matrix2','matrix/matrix2-api','matrix/m3','matrix/matrix3','matrix/matrix3-api','matrix/m4','matrix/matrix4','matrix/matrix4-api','matrix/t','matrix/transform','matrix/transform-api'],function ( require ) { - Object.defineProperty( this, 'Vector3', { - get: function() { - return _vector3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector3', { - get: function() { - return _vector3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Vector4', { - get: function() { - return _vector4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'vector4', { - get: function() { - return _vector4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Quaternion', { - get: function() { - return _quaternion.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'quaternion', { - get: function() { - return _quaternion; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix2', { - get: function() { - return _matrix2.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix2', { - get: function() { - return _matrix2; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix3', { - get: function() { - return _matrix3.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix3', { - get: function() { - return _matrix3; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Matrix4', { - get: function() { - return _matrix4.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'matrix4', { - get: function() { - return _matrix4; - }, - enumerable: true - }); - - Object.defineProperty( this, 'Transform', { - get: function() { - return _transform.$; - }, - enumerable: true - }); - Object.defineProperty( this, 'transform', { - get: function() { - return _transform; - }, - enumerable: true - }); - + var constants = require( "constants" ); + var equal = require( "equal" ); + + var V2 = require( "vector/v2" ); + var Vector2 = require( "vector/vector2" ); + var vector2 = require( "vector/vector2-api" ); + + var V3 = require( "vector/v3" ); + var Vector3 = require( "vector/vector3" ); + var vector3 = require( "vector/vector3-api" ); + + var V4 = require( "vector/v4" ); + var Vector4 = require( "vector/vector4" ); + var vector4 = require( "vector/vector4-api" ); + + var M2 = require( "matrix/m2" ); + var Matrix2 = require( "matrix/matrix2" ); + var matrix2 = require( "matrix/matrix2-api" ); + + var M3 = require( "matrix/m3" ); + var Matrix3 = require( "matrix/matrix3" ); + var matrix3 = require( "matrix/matrix3-api" ); + + var M4 = require( "matrix/m4" ); + var Matrix4 = require( "matrix/matrix4" ); + var matrix4 = require( "matrix/matrix4-api" ); + + var T = require( "matrix/t" ); + var Transform = require( "matrix/transform" ); + var transform = require( "matrix/transform-api" ); + + function extend( object, extra ) { + for ( var prop in extra ) { + if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { + object[prop] = extra[prop]; + } + } + } + + var _Math = function( options ) { + var FLOAT_ARRAY_ENUM = { + Float32: Float32Array, + Float64: Float64Array }; + this.FLOAT_ARRAY_ENUM = FLOAT_ARRAY_ENUM; + + var ARRAY_TYPE = this.ARRAY_TYPE = FLOAT_ARRAY_ENUM.Float32; + + extend( this, constants ); + this.equal = equal; + extend( this, { + V2: V2( ARRAY_TYPE ), + Vector2: Vector2( ARRAY_TYPE ), + vector2: vector2( ARRAY_TYPE ) + }); + extend( this, { + V3: V3( ARRAY_TYPE ), + Vector3: Vector3( ARRAY_TYPE ), + vector3: vector3( ARRAY_TYPE ) + }); + extend( this, { + V4: V4( ARRAY_TYPE ), + Vector4: Vector4( ARRAY_TYPE ), + vector4: vector4( ARRAY_TYPE ) + }); + extend( this, { + M2: M2( ARRAY_TYPE ), + Matrix2: Matrix2( ARRAY_TYPE ), + matrix2: matrix2( ARRAY_TYPE ) + }); + extend( this, { + M3: M3( ARRAY_TYPE ), + Matrix3: Matrix3( ARRAY_TYPE ), + matrix3: matrix3( ARRAY_TYPE ) + }); + extend( this, { + M4: M4( ARRAY_TYPE ), + Matrix4: Matrix4( ARRAY_TYPE ), + matrix4: matrix4( ARRAY_TYPE ) + }); + extend( this, { + T: T( ARRAY_TYPE ), + Transform: Transform( ARRAY_TYPE ), + transform: transform( ARRAY_TYPE ) + }); + }; - return new _Math(); + return new _Math(); }); - return require( "_math" ); + return require('_math'); })); if ( typeof define !== "function" ) { var define = require( "amdefine" )( module ); } +define('src/services/renderer',['require','base/service','CubicVR','src/services/target','core/event','_math'],function ( require ) { + + var Service = require( "base/service" ); + require( "CubicVR" ); + var Target = require( "src/services/target" ); + var Event = require( "core/event" ); + var math = require( "_math" ); + + var Renderer = function( scheduler, options ) { + options = options || {}; + + var schedules = { + "render": { + tags: ["@render", "graphics"], + dependsOn: [] + } + }; + Service.call( this, scheduler, schedules ); + + this.target = new Target( options.canvas ); + }; + + var convertedTransform = new math.T(); + + function render() { + var context = this.target.context; + var registeredComponents = this._registeredComponents; + var gl = context.GLCore.gl; + var spaces = {}; + var sIndex, sLength; + var component; + + // Update all graphics components + var updateEvent = new Event( 'Update', undefined, false ); + for( var componentType in registeredComponents ) { + for( var entityId in registeredComponents[componentType] ) { + component = registeredComponents[componentType][entityId]; + while( component.handleQueuedEvent() ) {} + updateEvent.dispatch( component ); + } + } + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + var cameraOwnerIds = Object.keys( registeredComponents["Camera"] || {} ); + cameraOwnerIds.forEach( function( id ) { + var ownerSpace = registeredComponents["Camera"][id].owner.space; + if( !spaces.hasOwnProperty( ownerSpace.id ) ) { + spaces[ownerSpace.id] = ownerSpace; + } + }); + var spaceIds = Object.keys( spaces ); + + for( sIndex = 0, sLength = spaceIds.length; sIndex < sLength; ++ sIndex ) { + var spaceId = spaceIds[sIndex]; + var space = spaces[spaceId]; + var i, l; + var cameraEntities = space.findAllWith( "Camera" ); + var modelEntities = space.findAllWith( "Model" ); + var lightEntities = space.findAllWith( "Light" ); + + // Handle lights for the current space + var cubicvrLights = []; + for( i = 0, l = lightEntities.length; i < l; ++ i ) { + var light = lightEntities[i].findComponent( "Light" ); + cubicvrLights.push( light._cubicvrLight ); + } + + // Render the space for each camera + for( i = 0, l = cameraEntities.length; i < l; ++ i ) { + var camera = cameraEntities[ i ].findComponent( "Camera" ); + + cubicvrLights.forEach( function( light ) { + light.prepare( camera._cubicvrCamera ); + }); + + for( var mi = 0, ml = modelEntities.length; mi < ml; ++mi ) { + var model = modelEntities[ mi ].findComponent( "Model" ); + var transform = modelEntities[ mi ].findComponent( "Transform" ); + math.matrix4.transpose(transform.worldMatrix(), convertedTransform); + + + model._cubicvrMesh.instanceMaterials = [model._cubicvrMaterialDefinition]; + + context.renderObject( + model._cubicvrMesh, + camera._cubicvrCamera, + convertedTransform, + cubicvrLights + ); + + model._cubicvrMesh.instanceMaterials = null; + } + } + } + } + + Renderer.prototype = new Service(); + Renderer.prototype.constructor = Renderer; + Renderer.prototype.render = render; + + return Renderer; + +}); +if ( typeof define !== "function" ) { + var define = require( "amdefine" )( module ); +} + +define('common/extend',['require'],function ( require ) { + + + function extend( object, extra ) { + for ( var prop in extra ) { + if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { + object[prop] = extra[prop]; + } + } + return object; + } + + return extend; + +}); + +if ( typeof define !== "function" ) { + var define = require( "amdefine" )( module ); +} + +define('base/component',['require','core/event'],function( require ) { + + var Event = require( "core/event" ); + + var Component = function( type, provider, dependsOn ) { + this.type = type; // String identifying the type of this component + this.provider = provider; // Reference to the object instance that provides + // this component + this.dependsOn = dependsOn || []; // List of component types that this + // component depends on + this.owner = null; // Reference to the entity instance that owns this + this._queuedEvents = []; // List of queued events + }; + + function setOwner( owner ) { + if( owner !== this.owner ) { + var previous = this.owner; + this.owner = owner; + var event = new Event( + 'ComponentOwnerChanged', + { + current: owner, + previous: previous + }, + false + ); + event.dispatch( this ); + } + } + + function handleEvent( event ) { + if( "on" + event.type in this ) { + if( event.queue ) { + this._queuedEvents.push( event ); + } else { + var handler = this["on" + event.type]; + try { + handler.call( this, event ); + } catch( error ) { + console.log( error ); + } + } + } + } + + function handleQueuedEvent() { + if( this._queuedEvents.length > 0 ) { + var event = this._queuedEvents.shift(); + if( "on" + event.type in this ) { + var handler = this["on" + event.type]; + try { + handler.call( this, event ); + } catch( error ) { + console.log( error ); + } + } + } + return this._queuedEvents.length; + } + + Component.prototype = { + setOwner: setOwner, + handleEvent: handleEvent, + handleQueuedEvent: handleQueuedEvent + }; + + return Component; + +}); +if ( typeof define !== "function" ) { + var define = require( "amdefine" )( module ); +} + define('src/components/camera',['require','common/extend','base/component','_math'],function( require ) { var extend = require( "common/extend" ); @@ -23604,9 +32825,9 @@ define('src/components/camera',['require','common/extend','base/component','_mat targeted: (options.targeted === undefined) ? false : options.targeted }); this._cubicvrCamera.parent = { - tMatrix: math.matrix4.identity + tMatrix: new math.T() }; - + this.target = [0, 0, 0]; this._targetHasChanged = false; @@ -23616,7 +32837,7 @@ define('src/components/camera',['require','common/extend','base/component','_mat Camera.prototype.constructor = Camera; function onUpdate( event ) { - this._cubicvrCamera.parent.tMatrix = this.owner.findComponent("Transform").absolute(); + math.matrix4.transpose(this.owner.findComponent( "Transform" ).worldMatrix(), this._cubicvrCamera.parent.tMatrix); if( this._targetHasChanged ) { this._cubicvrCamera.lookat( this.target ); this._targetHasChanged = false; @@ -23642,7 +32863,7 @@ define('src/components/camera',['require','common/extend','base/component','_mat } if( this.owner ) { - this._cubicvrCamera.parent.tMatrix = this.owner.findComponent("Transform").absolute(); + math.matrix4.transpose(this.owner.findComponent( "Transform" ).worldMatrix(), this._cubicvrCamera.parent.tMatrix); } if( this.owner === null && data.previous !== null ) { @@ -23873,7 +33094,7 @@ define('src/components/light',['require','src/resources/light-definition','base/ this._cubicvrLight = new service.target.context.Light(lightDefinition); this._cubicvrLight.parent = { - tMatrix: math.matrix4.identity + tMatrix: new math.T() }; for (var propertyIndex = 0; propertyIndex < properties.length; propertyIndex++){ @@ -23887,7 +33108,7 @@ define('src/components/light',['require','src/resources/light-definition','base/ for (var propertyIndex = 0; propertyIndex < properties.length; propertyIndex++){ this._cubicvrLight[properties[propertyIndex]] = this[properties[propertyIndex]]; } - this._cubicvrLight.parent.tMatrix = this.owner.findComponent( "Transform" ).absolute(); + math.matrix4.transpose(this.owner.findComponent( "Transform" ).worldMatrix(), this._cubicvrLight.parent.tMatrix); } function onEntitySpaceChanged( event ) { @@ -23897,7 +33118,7 @@ define('src/components/light',['require','src/resources/light-definition','base/ } if( this.owner ) { - this._cubicvrLight.parent.tMatrix = this.owner.findComponent( "Transform" ).absolute(); + math.matrix4.transpose(this.owner.findComponent( "Transform" ).worldMatrix(), this._cubicvrLight.parent.tMatrix); } if( data.previous !== null && data.current === null && this.owner !== null ) { @@ -23912,7 +33133,7 @@ define('src/components/light',['require','src/resources/light-definition','base/ } if( this.owner ) { - this._cubicvrLight.parent.tMatrix = this.owner.findComponent( "Transform" ).absolute(); + math.matrix4.transpose(this.owner.findComponent( "Transform" ).worldMatrix(), this._cubicvrLight.parent.tMatrix); } if( this.owner === null && data.previous !== null ) { diff --git a/gladius/package.json b/gladius/package.json index 761b891..0a80c57 100644 --- a/gladius/package.json +++ b/gladius/package.json @@ -1,7 +1,6 @@ { "name": "gladius", "volo": { - "baseUrl": "lib", - "url": ""https://github.com/downloads/gladiusjs/gladius/gladius-{version}.js" + "baseUrl": "lib" } } From e6610b59d1104ff462eae06c05b21e41d1b9843d Mon Sep 17 00:00:00 2001 From: David Perit Date: Fri, 24 Aug 2012 11:01:19 -0400 Subject: [PATCH 2/4] Added missing files --- .../assets/images/red-tank-diffuse.jpg | Bin 0 -> 32917 bytes gladius/examples/assets/procedural-sphere.js | 26 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 gladius/examples/assets/images/red-tank-diffuse.jpg create mode 100644 gladius/examples/assets/procedural-sphere.js diff --git a/gladius/examples/assets/images/red-tank-diffuse.jpg b/gladius/examples/assets/images/red-tank-diffuse.jpg new file mode 100644 index 0000000000000000000000000000000000000000..685cc4120cf97be9a565182d83c6e9576e2fafa5 GIT binary patch literal 32917 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 z Date: Fri, 24 Aug 2012 15:16:38 -0400 Subject: [PATCH 3/4] Added new version of gladius --- gladius/examples/tank/tank.js | 14 +++++++------- gladius/gladius-box2d.js | 16 ++++++++-------- gladius/gladius-core.js | 35 ++++++++++++++--------------------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/gladius/examples/tank/tank.js b/gladius/examples/tank/tank.js index fda977c..40839ca 100644 --- a/gladius/examples/tank/tank.js +++ b/gladius/examples/tank/tank.js @@ -260,7 +260,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { rotation-=tankRotationSpeed; } } - transform.directionToWorld(tankVelocity, tankVelocity); + transform.pointToWorld(tankVelocity, tankVelocity); physBody.setLinearVelocity(tankVelocity[0],tankVelocity[2]); physBody.setAngularVelocity(rotation); if (controller.states["TurnTurretLeft"] ) { @@ -294,7 +294,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { })}); physicsBody.tankBulletCollisions = 0; var barrelTransform = space.findNamed("tank-barrel").findComponent( "Transform"); - var bulletFiringPoint = math.vector3.add(barrelTransform.toWorldPoint(), barrelTransform.directionToWorld([0.3,0,0])); + var bulletFiringPoint = math.vector3.add(barrelTransform.toWorldPoint(), barrelTransform.pointToWorld([0.3,0,0])); var newBullet = new Entity("bullet", [ new engine.core.Transform(bulletFiringPoint), @@ -312,7 +312,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }; space.add(newBullet); bulletVelocity = [3,0,0]; - space.findNamed("tank-barrel").findComponent( "Transform").directionToWorld(bulletVelocity, bulletVelocity); + space.findNamed("tank-barrel").findComponent( "Transform").pointToWorld(bulletVelocity, bulletVelocity); var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); impEvent.dispatch(newBullet); } @@ -491,7 +491,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } if (!redTank.stunned){ //Rotate the red turret to point at the green tank - var greenTankInRedTankTurretLocalSpace = redTankTurretTransform.transformToLocal(greenTankTransform); + var greenTankInRedTankTurretLocalSpace = redTankTurretTransform.relativeTo(greenTankTransform); //We are multiplying the z coordinate by -1 because Math.atan2 expects (y,x), and up on the screen is negative for z but positive for y var directionOfGreenTank = Math.atan2(greenTankInRedTankTurretLocalSpace[2] * -1, greenTankInRedTankTurretLocalSpace[0]); var currentDirection = redTankTurretTransform.rotation.y; @@ -517,7 +517,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { filter:{categoryBits:2, maskBits:15} })}); bulletBody.tankBulletCollisions = 0; - var bulletFiringPoint = math.vector3.add(redTankBarrelTransform.toWorldPoint(), redTankBarrelTransform.directionToWorld([0.3,0,0])); + var bulletFiringPoint = math.vector3.add(redTankBarrelTransform.toWorldPoint(), redTankBarrelTransform.pointToWorld([0.3,0,0])); var newBullet = new Entity("bullet", [ new engine.core.Transform(bulletFiringPoint), @@ -535,7 +535,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }; space.add(newBullet); bulletVelocity = [2,0,0]; - redTankBarrelTransform.directionToWorld(bulletVelocity, bulletVelocity); + redTankBarrelTransform.pointToWorld(bulletVelocity, bulletVelocity); var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); impEvent.dispatch(newBullet); } @@ -573,7 +573,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { if (redTank.timeToRotate < 0){ redTank.doneRotation = true; redTank.tankVelocity = [tankMovementSpeed, 0, 0]; - redTankTransform.directionToWorld(redTank.tankVelocity, redTank.tankVelocity); + redTankTransform.pointToWorld(redTank.tankVelocity, redTank.tankVelocity); physicsBody.setAngularVelocity(0); physicsBody.setLinearVelocity(redTank.tankVelocity[0], redTank.tankVelocity[2]); } diff --git a/gladius/gladius-box2d.js b/gladius/gladius-box2d.js index 084ceb5..e0c3698 100644 --- a/gladius/gladius-box2d.js +++ b/gladius/gladius-box2d.js @@ -99864,7 +99864,7 @@ define('src/components/body',['require','box2d','common/extend','base/component' Body.prototype = new Component(); Body.prototype.constructor = Body; - var linearVector = new Box2D.b2Vec2( 0, 0 ); + var b2Vector = new Box2D.b2Vec2( 0, 0 ); function setAngularVelocity(rotation){ this.box2dBody.SetAngularVelocity(rotation); @@ -99873,19 +99873,19 @@ define('src/components/body',['require','box2d','common/extend','base/component' function setLinearVelocity(arg1, arg2) { var argc = arguments.length; if( 1 === argc ) { - linearVector.Set( arg1[0], arg1[1] ); + b2Vector.Set( arg1[0], arg1[1] ); }else{ - linearVector.Set( arg1, arg2); + b2Vector.Set( arg1, arg2); } - this.box2dBody.SetLinearVelocity( linearVector ); - linearVector.Set( 0, 0 ); + this.box2dBody.SetLinearVelocity( b2Vector ); + b2Vector.Set( 0, 0 ); } function onLinearImpulse( event ) { var impulse = event.data.impulse; - linearVector.Set( impulse[0], impulse[1] ); - this.box2dBody.ApplyLinearImpulse( linearVector, this.box2dBody.GetPosition() ); - linearVector.Set( 0, 0 ); + b2Vector.Set( impulse[0], impulse[1] ); + this.box2dBody.ApplyLinearImpulse( b2Vector, this.box2dBody.GetPosition() ); + b2Vector.Set( 0, 0 ); } function onAngularImpulse( event ) { diff --git a/gladius/gladius-core.js b/gladius/gladius-core.js index b6c09ad..d5be028 100644 --- a/gladius/gladius-core.js +++ b/gladius/gladius-core.js @@ -14178,21 +14178,14 @@ define('core/components/transform',['require','_math','common/extend','base/comp } } - //This calculates the rotation of world space relative to the object - //It is wrong! - function computeLocalRotation(){ - //TODO: Add caching of results in here once we have a way of detecting changes in the parents - if( this.owner && this.owner.parent && - this.owner.parent.hasComponent( "Transform" ) ) { - return math.matrix4.multiply(math.transform.rotate(this._rotation.buffer), - this.owner.parent.findComponent( "Transform").localRotation()); - }else{ - return math.transform.rotate(this._rotation.buffer); - } + //TODO: Should produce a unit vector showing the orientation of things in world space + function directionToWorld(){ + } - function directionToWorld(direction, result) { + function pointToWorld(direction, result) { result = result || new math.V3(); + direction = direction || new math.V3(); math.matrix4.multiply( computeWorldRotation.call(this), math.transform.translate( direction ), @@ -14201,15 +14194,15 @@ define('core/components/transform',['require','_math','common/extend','base/comp return result; } - function directionToLocal(direction, result) { + function pointToLocal(direction, result) { result = result || new math.V3(); if( this.owner && this.owner.parent && this.owner.parent.hasComponent( "Transform" ) ) { var thisParentWorldMatrix = this.owner.parent.findComponent( "Transform").worldMatrix(); //Multiply the inverse of the parent's world matrix by the other transform's world matrix, - // putting the result in thisWorldPositionMatrix + // putting the result in the temp matrix //Solution grabbed from http://www.macaronikazoo.com/?p=419 - math.matrix4.multiply(math.matrix4.inverse(thisParentWorldMatrix,this._tempMatrix), math.matrix4.translate(direction), this._tempMatrix); + math.matrix4.multiply(math.matrix4.inverse(thisParentWorldMatrix,this._tempMatrix), math.transform.translate(direction), this._tempMatrix); //Subtract this turret's position so that everything is offset properly math.vector3.set(result, this._tempMatrix[3] - this._position.buffer[0], this._tempMatrix[7] - this._position.buffer[1], this._tempMatrix[11] - this._position.buffer[2]); } @@ -14224,7 +14217,7 @@ define('core/components/transform',['require','_math','common/extend','base/comp return [worldMatrix[3], worldMatrix[7], worldMatrix[11]]; } - function transformToLocal(otherTransform, result) + function relativeTo(otherTransform, result) { result = result || new math.V3(); var otherWorldMatrix = otherTransform.worldMatrix(); @@ -14232,7 +14225,7 @@ define('core/components/transform',['require','_math','common/extend','base/comp this.owner.parent.hasComponent( "Transform" ) ) { var thisParentWorldMatrix = this.owner.parent.findComponent( "Transform").worldMatrix(); //Multiply the inverse of the parent's world matrix by the other transform's world matrix, - // putting the result in thisWorldPositionMatrix + // putting the result in the temp matrix // Solution grabbed from http://www.macaronikazoo.com/?p=419 math.matrix4.multiply(math.matrix4.inverse(thisParentWorldMatrix,this._tempMatrix), otherWorldMatrix, this._tempMatrix); //Subtract this turret's position so that everything is offset properly @@ -14248,13 +14241,13 @@ define('core/components/transform',['require','_math','common/extend','base/comp //TODO: worldMatrix and localMatrix look like property accessors from the outside but are actually methods. This should be changed, either so that they are accessed like properties or look like methods worldMatrix: computeWorldMatrix, localMatrix: computeLocalMatrix, - directionToLocal: directionToLocal, - directionToWorld: directionToWorld, + pointToLocal: pointToLocal, + pointToWorld: pointToWorld, + directionToWorld: undefined, //Same thing goes for this one. worldRotation: computeWorldRotation, - localRotation: computeLocalRotation, + relativeTo: relativeTo, toWorldPoint: toWorldPoint, - transformToLocal: transformToLocal, lookAt: undefined, target: undefined, // Direction constants From b62dc02e1554846038eced715d27adf551c98e74 Mon Sep 17 00:00:00 2001 From: David Perit Date: Fri, 21 Sep 2012 16:01:50 -0400 Subject: [PATCH 4/4] Added updated versions of tank example and box2d plugin --- gladius/examples/tank/tank.js | 70 +++++++++++++++++------------------ gladius/gladius-box2d.js | 28 ++------------ 2 files changed, 37 insertions(+), 61 deletions(-) diff --git a/gladius/examples/tank/tank.js b/gladius/examples/tank/tank.js index 40839ca..3e70b85 100644 --- a/gladius/examples/tank/tank.js +++ b/gladius/examples/tank/tank.js @@ -38,9 +38,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } engine.registerExtension( inputExtension, inputOptions ); - //Need to find a way to make this property access longer :) - engine.registerExtension( box2dExtension, - {resolver: {dimensionMap: box2dExtension.services.resolver.service.prototype.DimensionMaps.XZ}}); + engine.registerExtension( box2dExtension ); var cubicvr = engine.findExtension( "gladius-cubicvr" ); var input = engine.findExtension( "gladius-input" ); @@ -67,7 +65,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { [ { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=1.0&width=0.25&depth=0.5", + url: "../assets/procedural-prism.js?length=1.0&width=0.5&depth=0.25", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankBody = mesh; @@ -77,7 +75,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=0.85&width=0.35&depth=0.2", + url: "../assets/procedural-prism.js?length=0.85&width=0.2&depth=0.35", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTread = mesh; @@ -87,7 +85,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=0.5&width=0.15&depth=0.35", + url: "../assets/procedural-prism.js?length=0.5&width=0.35&depth=0.15", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTurret = mesh; @@ -97,7 +95,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }, { type: cubicvr.Mesh, - url: "../assets/procedural-prism.js?length=0.4&width=0.05&depth=0.1", + url: "../assets/procedural-prism.js?length=0.4&width=0.1&depth=0.05", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankBarrel = mesh; @@ -229,7 +227,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { if (greenTank.stunnedTime < 0){ greenTank.stunned = false; }else{ - turretTransform.rotation.y += greenTank.stunnedTurretRotationSpeed * elapsedTime * greenTank.stunnedTurretRotationDirection; + turretTransform.rotation.z += greenTank.stunnedTurretRotationSpeed * elapsedTime * greenTank.stunnedTurretRotationDirection; } }else if( this.owner.hasComponent( "Controller" ) ) { var controller = this.owner.findComponent( "Controller" ); @@ -248,26 +246,26 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } if( controller.states["TurnLeft"] ) { if( controller.states["StrafeModifier"] ) { - tankVelocity[2]-=tankMovementSpeed; + tankVelocity[1]-=tankMovementSpeed; } else { rotation+=tankRotationSpeed; } } if( controller.states["TurnRight"] ) { if( controller.states["StrafeModifier"] ) { - tankVelocity[2]+=tankMovementSpeed; + tankVelocity[1]+=tankMovementSpeed; } else { rotation-=tankRotationSpeed; } } transform.pointToWorld(tankVelocity, tankVelocity); - physBody.setLinearVelocity(tankVelocity[0],tankVelocity[2]); + physBody.setLinearVelocity(tankVelocity[0],tankVelocity[1]); physBody.setAngularVelocity(rotation); if (controller.states["TurnTurretLeft"] ) { - turretTransform.rotation.add([0, (space.clock.delta/1000) * greenTankTurretRotationSpeed, 0]); + turretTransform.rotation.add([0, 0, (space.clock.delta/1000) * greenTankTurretRotationSpeed]); } if (controller.states["TurnTurretRight"] ) { - turretTransform.rotation.add([0, (space.clock.delta/1000) * -greenTankTurretRotationSpeed, 0]); + turretTransform.rotation.add([0, 0, (space.clock.delta/1000) * -greenTankTurretRotationSpeed]); } } @@ -313,7 +311,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.add(newBullet); bulletVelocity = [3,0,0]; space.findNamed("tank-barrel").findComponent( "Transform").pointToWorld(bulletVelocity, bulletVelocity); - var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); + var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[1]]}); impEvent.dispatch(newBullet); } } @@ -347,7 +345,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { )); space.add(new Entity(name + "-tread", [ - new engine.core.Transform([0, 0, 0.4]), + new engine.core.Transform([0, 0.4, 0]), new cubicvr.Model(resources.tankTread, material) ], [name], @@ -355,7 +353,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { )); space.add(new Entity(name + "-tread", [ - new engine.core.Transform([0, 0, -0.4]), + new engine.core.Transform([0, -0.4, 0]), new cubicvr.Model(resources.tankTread, material) ], [name], @@ -363,7 +361,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { )); space.add(new Entity(name+"-turret", [ - new engine.core.Transform([-0.1, 0.3, 0]), + new engine.core.Transform([-0.1, 0, 0.3]), new cubicvr.Model(resources.tankTurret, material) ], [name], @@ -378,8 +376,8 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.findNamed(name + "-turret") )); } - createTank("tank", [-3,0,-3], resources.material, true, 8); - createTank("red-tank", [1,0,0], resources.redMaterial, false, 16); + createTank("tank", [-3,-3], resources.material, true, 8); + createTank("red-tank", [3,3,0], resources.redMaterial, false, 16); var redTank = space.findNamed( "red-tank" ); redTank.doneRotation = true; redTank.doneMovement = true; @@ -418,28 +416,28 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.add( new Entity( "wallLeft", [ - new engine.core.Transform([-5,0,0], [0,math.TAU/4,0]), + new engine.core.Transform([-5,0,0], [0,0,math.TAU/4]), new cubicvr.Model(resources.wall, resources.wallMaterial), new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) ] )); space.add( new Entity( "wallRight", [ - new engine.core.Transform([5,0,0], [0,math.TAU/4,0]), + new engine.core.Transform([5,0,0], [0,0,math.TAU/4]), new cubicvr.Model(resources.wall, resources.wallMaterial), new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) ] )); space.add( new Entity( "wallTop", [ - new engine.core.Transform([0,0,-5], [0,0,0]), + new engine.core.Transform([0,-5,0], [math.TAU/4,0,0]), new cubicvr.Model(resources.wall, resources.wallMaterial), new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) ] )); space.add( new Entity( "wallBottom", [ - new engine.core.Transform([0,0,5], [0,0,0]), + new engine.core.Transform([0,5,0], [math.TAU/4,0,0]), new cubicvr.Model(resources.wall, resources.wallMaterial), new box2d.Body({bodyDefinition: bodyDefinition, fixtureDefinition: fixtureDefinition}) ] @@ -447,7 +445,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.add( new Entity( "camera", [ - new engine.core.Transform( [0, 10, 0], [-math.TAU/4, 0, 0] ), + new engine.core.Transform( [0, 0, 10], [0, 0, 0] ), new cubicvr.Camera(), new cubicvr.Light(lightDefinition) ] @@ -486,15 +484,14 @@ document.addEventListener( "DOMContentLoaded", function( e ) { redTank.stunned = false; redTank.doneMovement = true; }else{ - redTankTurretTransform.rotation.y += redTank.stunnedTurretRotationSpeed * elapsedTime * redTank.stunnedTurretRotationDirection; + redTankTurretTransform.rotation.z += redTank.stunnedTurretRotationSpeed * elapsedTime * redTank.stunnedTurretRotationDirection; } } if (!redTank.stunned){ //Rotate the red turret to point at the green tank var greenTankInRedTankTurretLocalSpace = redTankTurretTransform.relativeTo(greenTankTransform); - //We are multiplying the z coordinate by -1 because Math.atan2 expects (y,x), and up on the screen is negative for z but positive for y - var directionOfGreenTank = Math.atan2(greenTankInRedTankTurretLocalSpace[2] * -1, greenTankInRedTankTurretLocalSpace[0]); - var currentDirection = redTankTurretTransform.rotation.y; + var directionOfGreenTank = Math.atan2(greenTankInRedTankTurretLocalSpace[1], greenTankInRedTankTurretLocalSpace[0]); + var currentDirection = redTankTurretTransform.rotation.z; var differenceBetweenDirections = removeExcessRotation(currentDirection - directionOfGreenTank); var newRotation; if (differenceBetweenDirections > 0 && differenceBetweenDirections > (redTankTurretRotationSpeed * elapsedTime)){ @@ -504,7 +501,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }else{ newRotation = directionOfGreenTank; } - redTankTurretTransform.rotation.y = newRotation; + redTankTurretTransform.rotation.z = newRotation; if (space.clock.time > lastRedBulletTime + redTankFiringInterval){ lastRedBulletTime = space.clock.time; @@ -536,15 +533,15 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.add(newBullet); bulletVelocity = [2,0,0]; redTankBarrelTransform.pointToWorld(bulletVelocity, bulletVelocity); - var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[2]]}); + var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[1]]}); impEvent.dispatch(newBullet); } if (redTank.doneMovement){ //Done moving, make up a new destination newPosition[0] = getRandom(-3, 3); - newPosition[1] = position.y; - newPosition[2] = getRandom(-3, 3); + newPosition[1] = getRandom(-3, 3); + newPosition[2] = position.z; //Uncomment this to make bullets appear at the tank's new destination. Useful for debugging // space.add(new Entity("bullet", // [ @@ -553,15 +550,16 @@ document.addEventListener( "DOMContentLoaded", function( e ) { // ] // )); - var currentRotation = redTankTransform.rotation.y; - var directionToNewPosition = Math.atan2(newPosition[2] - position.z, newPosition[0] - position.x); + //Multiply by negative 1 due to handedness differences between what atan2 will give us and what rotation.z is + var currentRotation = redTankTransform.rotation.z * -1; + var directionToNewPosition = Math.atan2(newPosition[1] - position.y, newPosition[0] - position.x); changeInDirection = removeExcessRotation(directionToNewPosition + currentRotation); //Because we are only telling the tank to stop rotating/moving at the end of a frame, this does NOT result in deterministic movement //The tank will turn/move more or less depending on how high the frame rate is redTank.timeToRotate = Math.abs(changeInDirection)/tankRotationSpeed; redTank.timeToMove = position.distance(newPosition) / tankMovementSpeed; - redTank.rotationDirection = changeInDirection > 0 ? -1 : 1; + redTank.rotationDirection = changeInDirection > 0 ? 1 : -1; redTank.doneRotation = false; redTank.doneMovement = false; physicsBody.setAngularVelocity(tankRotationSpeed * redTank.rotationDirection); @@ -575,7 +573,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { redTank.tankVelocity = [tankMovementSpeed, 0, 0]; redTankTransform.pointToWorld(redTank.tankVelocity, redTank.tankVelocity); physicsBody.setAngularVelocity(0); - physicsBody.setLinearVelocity(redTank.tankVelocity[0], redTank.tankVelocity[2]); + physicsBody.setLinearVelocity(redTank.tankVelocity[0], redTank.tankVelocity[1]); } } //Move until we reach the desired destination diff --git a/gladius/gladius-box2d.js b/gladius/gladius-box2d.js index e0c3698..7d06dff 100644 --- a/gladius/gladius-box2d.js +++ b/gladius/gladius-box2d.js @@ -99615,7 +99615,6 @@ define('src/services/resolver',['require','base/service','core/event','_math','b this.gravity = new Box2D.b2Vec2(); this.world = new Box2D.b2World( this.gravity ); - this.dimensionMap = options.dimensionMap || 0; this._timeStep = 30; // time step, in milliseconds this._timeRemaining = 0; // time remaining from last frame, in milliseconds @@ -99672,12 +99671,6 @@ define('src/services/resolver',['require','base/service','core/event','_math','b var totalForce = new math.Vector2(); - var DimensionMaps = { - XY: 0, - XZ: 1, - YZ: 2 - }; - function resolve() { var component; @@ -99723,7 +99716,6 @@ define('src/services/resolver',['require','base/service','core/event','_math','b Resolver.prototype = new Service(); Resolver.prototype.constructor = Resolver; Resolver.prototype.resolve = resolve; - Resolver.prototype.DimensionMaps = DimensionMaps; return Resolver; @@ -99899,16 +99891,8 @@ define('src/components/body',['require','box2d','common/extend','base/component' var transform = this.owner.findComponent( "Transform" ); //Note: It is currently okay to read from buffers, but writing to them will result in things breaking - if (this.service.dimensionMap === this.service.DimensionMaps.XY){ - transform.position = [ position2.get_x(), position2.get_y(), transform.position.buffer[2] ]; - transform.rotation.z = angle2; - }else if (this.service.dimensionMap === this.service.DimensionMaps.XZ){ - transform.position = [ position2.get_x(), transform.position.buffer[1], position2.get_y()]; - transform.rotation.y = angle2; - }else{ - transform.position = [transform.position.buffer[0], position2.get_y(), position2.get_x()]; - transform.rotation.x = angle2; - } + transform.position = [ position2.get_x(), position2.get_y(), transform.position.buffer[2] ]; + transform.rotation.z = angle2; } function onEntitySpaceChanged( event ) { @@ -99937,13 +99921,7 @@ define('src/components/body',['require','box2d','common/extend','base/component' if( this.owner ) { var transform = this.owner.findComponent( 'Transform' ); //Note: It is currently okay to read from buffers, but writing to them will result in things breaking - if (this.service.dimensionMap === this.service.DimensionMaps.XY){ - this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[0], transform.position.buffer[1] ), transform.rotation.buffer[2] ); - }else if (this.service.dimensionMap === this.service.DimensionMaps.XZ){ - this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[0], transform.position.buffer[2] ), transform.rotation.buffer[1] ); - }else{ - this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[2], transform.position.buffer[1] ), transform.rotation.buffer[0] ); - } + this.box2dBody.SetTransform( new Box2D.b2Vec2( transform.position.buffer[0], transform.position.buffer[1] ), transform.rotation.buffer[2] ); } if( this.owner === null && data.previous !== null ) {