diff --git a/examples/tank.png b/examples/tank.png index 48eec1c..bde4866 100644 Binary files a/examples/tank.png and b/examples/tank.png differ diff --git a/gladius/README.md b/gladius/README.md index d926a48..8004f98 100644 --- a/gladius/README.md +++ b/gladius/README.md @@ -30,7 +30,9 @@ Start by cloning the repository or downloading a zipped version from github. Inside the project directory you'll find pre-built versions of the following modules: * gladius-core: the engine core; you'll definitely need to load this -* gladius-cubicvr: CubicVR rendering backend +* gladius-cubicvr: CubicVR renderer +* gladius-box2d: Box2D physics +* gladius-input: user input We tested the examples with these module versions. If you build your own modules, the examples might still work, but they also might not. 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 0000000..685cc41 Binary files /dev/null and b/gladius/examples/assets/images/red-tank-diffuse.jpg differ diff --git a/gladius/examples/assets/procedural-sphere.js b/gladius/examples/assets/procedural-sphere.js new file mode 100644 index 0000000..aa71436 --- /dev/null +++ b/gladius/examples/assets/procedural-sphere.js @@ -0,0 +1,26 @@ +function proc( options ) { + + options = options || {}; + options.type = options.type || "sphere"; + options.radius = options.radius || 0.5; + options.latDetail = options.latDetail || 24; + options.lonDetail = options.lonDetail || 24; + + var mesh = + { + primitive: { + type: options.type, + radius: options.radius, + lat: options.latDetail, + lon: options.lonDetail, + uvmapper: { + projectionMode: "cubic", + scale: [1, 1, 1] + } + }, + compile: true + }; + + return mesh; + +} \ No newline at end of file diff --git a/gladius/examples/camera-rotate/camera-rotate.js b/gladius/examples/camera-rotate/camera-rotate.js index 676785b..86931dd 100644 --- a/gladius/examples/camera-rotate/camera-rotate.js +++ b/gladius/examples/camera-rotate/camera-rotate.js @@ -111,13 +111,9 @@ document.addEventListener( "DOMContentLoaded", function( e ) { space.add( parentCube ); var task = new engine.FunctionTask( function() { - var cubeRotation = new engine.math.Vector3( space.findNamed( "cube" ).findComponent( "Transform" ).rotation ); - cubeRotation = engine.math.vector3.add( cubeRotation, [0, space.clock.delta * 0.0003, 0] ); - space.findNamed( "cube" ).findComponent( "Transform" ).setRotation( cubeRotation ); + space.findNamed( "cube" ).findComponent( "Transform" ).rotation.y += space.clock.delta * 0.0003; - var cameraRotation = new engine.math.Vector3( space.findNamed( "camera" ).findComponent( "Transform" ).rotation ); - cameraRotation = engine.math.vector3.add( cameraRotation, [0, space.clock.delta * 0.0003, 0] ); - space.findNamed( "camera" ).findComponent( "Transform" ).setRotation( cameraRotation ); + space.findNamed( "camera" ).findComponent( "Transform" ).rotation.y += space.clock.delta * 0.0003; }, { tags: ["@update"] }); diff --git a/gladius/examples/cube-collision/cube-collision.js b/gladius/examples/cube-collision/cube-collision.js index db393e7..d22e711 100644 --- a/gladius/examples/cube-collision/cube-collision.js +++ b/gladius/examples/cube-collision/cube-collision.js @@ -32,12 +32,12 @@ document.addEventListener( "DOMContentLoaded", function( e ) { var box2dOptions = { resolver: { - gravity: [0,-0.5] + dimensionMap: box2dExtension.services.resolver.service.prototype.DimensionMaps.XY } }; engine.registerExtension( cubicvrExtension, cubicvrOptions ); - engine.registerExtension( box2dExtension);//, box2dOptions); + engine.registerExtension( box2dExtension, box2dOptions); var resources = {}; @@ -102,13 +102,14 @@ document.addEventListener( "DOMContentLoaded", function( e ) { var lightDefinition = new cubicvr.LightDefinition({ intensity: 1, + distance: 20, light_type: cubicvr.LightDefinition.LightTypes.POINT, method: cubicvr.LightDefinition.LightingMethods.DYNAMIC }); space.add( new engine.Entity( "camera", [ - new engine.core.Transform( [0, 0, 5] ), + new engine.core.Transform( [0, 0, 10] ), new cubicvr.Light( lightDefinition ), new cubicvr.Camera( { targeted:false @@ -117,7 +118,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { )); var bodyDefinition = new box2d.BodyDefinition(); - var fixtureDefinition = new box2d.FixtureDefinition({shape:new box2d.BoxShape(0.25,0.25)}); + var fixtureDefinition = new box2d.FixtureDefinition({shape:new box2d.BoxShape(1,1)}); for (var cubeIndex = 0; cubeIndex < 5; cubeIndex++){ @@ -132,7 +133,7 @@ document.addEventListener( "DOMContentLoaded", function( e ) { }; var firstCube = new engine.Entity( "cube1", [ - new engine.core.Transform( [3 + cubeIndex * 1.5, 0.125, 0], [0, 0, 0], [0.5, 0.5, 0.5] ), + new engine.core.Transform( [3 + cubeIndex * 3, 0.25, 0], [0, 0, 0]), firstBody, new cubicvr.Model( resources.mesh, resources.material ) ] @@ -151,14 +152,14 @@ document.addEventListener( "DOMContentLoaded", function( e ) { var secondCube = new engine.Entity( "cube2", [ - new engine.core.Transform( [-3 - cubeIndex * 1.5, -0.125, 0], [0, 0, 0], [0.5, 0.5, 0.5] ), + new engine.core.Transform( [-3 - cubeIndex * 3, -0.25, 0], [0, 0, 0]), secondBody, new cubicvr.Model( resources.mesh, resources.material ) ] ); space.add( secondCube ); - new engine.Event("LinearImpulse", {impulse: [-0.25,0]}).dispatch(firstCube); - new engine.Event("LinearImpulse", {impulse: [0.25,0]}).dispatch(secondCube); + new engine.Event("LinearImpulse", {impulse: [-1,0]}).dispatch(firstCube); + new engine.Event("LinearImpulse", {impulse: [1,0]}).dispatch(secondCube); } var task = new engine.FunctionTask( function() { diff --git a/gladius/examples/cube-impulse/cube-impulse.js b/gladius/examples/cube-impulse/cube-impulse.js index f916a08..e370ce7 100644 --- a/gladius/examples/cube-impulse/cube-impulse.js +++ b/gladius/examples/cube-impulse/cube-impulse.js @@ -122,22 +122,25 @@ document.addEventListener( "DOMContentLoaded", function( e ) { var cubePosition = new engine.math.Vector3( space.findNamed( "cube").findComponent( "Transform").position); // var camera = space.findNamed("camera").findComponent("Camera"); // camera.setTarget(cubePosition); - if (cubePosition[1] < -1.5){ + + var cubeY = cubePosition.y; + var cubeX = cubePosition.x; + if (cubeY < -1.5){ var impEvent = new engine.Event('LinearImpulse',{impulse: [getRandom(0,0.1), getRandom(0,1)]}); impEvent.dispatch(parentCube); } - if (cubePosition[1] > 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..3e70b85 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,34 @@ document.addEventListener( "DOMContentLoaded", function( e ) { } engine.registerExtension( inputExtension, inputOptions ); + engine.registerExtension( box2dExtension ); + 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.5&depth=0.25", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankBody = mesh; @@ -59,7 +75,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.2&depth=0.35", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTread = mesh; @@ -69,7 +85,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.35&depth=0.15", load: engine.loaders.procedural, onsuccess: function( mesh ) { resources.tankTurret = mesh; @@ -79,14 +95,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.1&depth=0.05", 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 +133,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 +178,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.z += 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[1]-=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[1]+=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.pointToWorld(tankVelocity, tankVelocity); + physBody.setLinearVelocity(tankVelocity[0],tankVelocity[1]); + physBody.setAngularVelocity(rotation); + if (controller.states["TurnTurretLeft"] ) { + turretTransform.rotation.add([0, 0, (space.clock.delta/1000) * greenTankTurretRotationSpeed]); + } + if (controller.states["TurnTurretRight"] ) { + turretTransform.rotation.add([0, 0, (space.clock.delta/1000) * -greenTankTurretRotationSpeed]); + } + } }, "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.pointToWorld([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").pointToWorld(bulletVelocity, bulletVelocity); + var impEvent = new engine.Event('LinearImpulse',{impulse: [bulletVelocity[0], bulletVelocity[1]]}); + 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.4, 0]), + new cubicvr.Model(resources.tankTread, material) + ], + [name], + space.findNamed(name + "-body") + )); + space.add(new Entity(name + "-tread", + [ + new engine.core.Transform([0, -0.4, 0]), + new cubicvr.Model(resources.tankTread, material) + ], + [name], + space.findNamed(name + "-body") + )); + space.add(new Entity(name+"-turret", + [ + new engine.core.Transform([-0.1, 0, 0.3]), + 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,-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; + 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,0,math.TAU/4]), + 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,0,math.TAU/4]), + 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,-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( "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,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( "camera", [ - new engine.core.Transform( [0, 0, 0] ), - new cubicvr.Camera({ - targeted: true - }), - new cubicvr.Light() + new engine.core.Transform( [0, 0, 10], [0, 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.z += redTank.stunnedTurretRotationSpeed * elapsedTime * redTank.stunnedTurretRotationDirection; + } + } + if (!redTank.stunned){ + //Rotate the red turret to point at the green tank + var greenTankInRedTankTurretLocalSpace = redTankTurretTransform.relativeTo(greenTankTransform); + 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)){ + newRotation = currentDirection - (redTankTurretRotationSpeed * elapsedTime); + }else if (differenceBetweenDirections < 0 && (-differenceBetweenDirections) > (redTankTurretRotationSpeed * elapsedTime)){ + newRotation = currentDirection + (redTankTurretRotationSpeed * elapsedTime); + }else{ + newRotation = directionOfGreenTank; + } + redTankTurretTransform.rotation.z = 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.pointToWorld([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.pointToWorld(bulletVelocity, bulletVelocity); + 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] = 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", +// [ +// new engine.core.Transform(newPosition), +// new cubicvr.Model(resources.bullet, resources.bulletMaterial) +// ] +// )); + + //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.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.pointToWorld(redTank.tankVelocity, redTank.tankVelocity); + physicsBody.setAngularVelocity(0); + physicsBody.setLinearVelocity(redTank.tankVelocity[0], redTank.tankVelocity[1]); + } + } + //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..7d06dff 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); - extend: function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } //if - } //for - } //extend + 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]; }; -}); + } -/*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; + } + + 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 vector; + 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; + + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/matrix3-api',['require','common/not-implemented','matrix/m3'],function ( require ) { -define('vector/vector2',['require','./vector','../constants'],function ( require ) { + return function( FLOAT_ARRAY_TYPE ) { - return function( FLOAT_ARRAY_TYPE ) { + var notImplemented = require( "common/not-implemented" ); + var M3 = require( "matrix/m3" )( FLOAT_ARRAY_TYPE ); - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); - var constants = require( '../constants' )(); + function add( m1, m2, result ) { + result = result || new M3(); - var Vector2 = function() { - if( 0 === arguments.length ) { - return vector.$( 2, [0, 0] ); - } else { - return vector.$( 2, arguments ); - } - }; + 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 vector2 = { - - $: Vector2, - - add: function( v1, v2, result ) { - result = result || Vector2(); + // 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; - 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 ) { - var nV1 = Vector2(); - var nV2 = Vector2(); + return result; + } - vector.normalize(v1, nV1); - vector.normalize(v2, nV2); + 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( vector.dot( nV1, nV2 ) ); - }, + 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]; - dot: vector.dot, + return result; + } - equal: vector.equal, + function transpose( m, result ) { + if( m && m === result ) { + var a01 = m[1], a02 = m[2], + a12 = m[5]; - length: vector.length, + result[1] = m[3]; + result[2] = m[6]; + result[3] = a01; + result[5] = m[7]; + result[6] = a02; + result[7] = a12; - 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 ); - }, + result = result || new M3(); - normalize: function( v, result ) { - result = result || Vector2(); - var len = vector.length(v); + 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]; - result[0] = v[0]/len; - result[1] = v[1]/len; + return result; + } - 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(); + 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 vector.subtract( v1, v2, result ); - } - - }; - - Object.defineProperty( vector2, 'x', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); + }; - Object.defineProperty( vector2, 'u', { - get: function() { - return Vector2( [1, 0] ); - }, - enumerable: true - }); +}); +define('matrix/matrix3',['require','../../lib/lodash','common/not-implemented','matrix/m3','matrix/matrix3-api','matrix/matrix'],function ( require ) { - Object.defineProperty( vector2, 'y', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + 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]; + } - Object.defineProperty( vector2, 'v', { - get: function() { - return Vector2( [0, 1] ); - }, - enumerable: true - }); + function getValue( index ) { + return this.buffer[index]; + } - Object.defineProperty( vector2, 'zero', { - get: function() { - return Vector2( [0, 0] ); - }, - enumerable: true - }); + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - Object.defineProperty( vector2, 'one', { - get: function() { - return Vector2( [1, 1] ); - }, - enumerable: true - }); + function updateViews() { + var i; + for( i = 0; i < 3; ++ i ) { + this._views[i] = new Matrix3View( this, this.buffer, + i*3, (i+1)*3 ); + } + } - return vector2; + 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 ) + } + }); }; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + 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(); + } -define('vector/vector3',['require','./vector'],function ( require ) { + Object.defineProperties( this, { + "0": { + get: getView.bind( this, 0 ) + }, + "1": { + get: getView.bind( this, 1 ) + }, + "2": { + get: getView.bind( this, 2 ) + } + }); - return function( FLOAT_ARRAY_TYPE ) { + this._views = []; - var vector = require( './vector' )( FLOAT_ARRAY_TYPE ); + updateViews.call( this ); - var Vector3 = function() { - if( 0 === arguments.length ) { - return vector.$( 3, [0, 0, 0] ); - } else { - return vector.$( 3, arguments ); - } - }; + this.modified = true; + }; + Matrix3.prototype = new Matrix(); + Matrix3.prototype.constructor = Matrix3; - var vector3 = { - - $: Vector3, + function add( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - add: function( v1, v2, result ) { - result = result || Vector3(); + result = result || this; + matrix3.add( this.buffer, other, result.buffer ); + result.modified = true; - return vector.add( v1, v2, result ); - }, + return this; + } - angle: function( v1, v2 ) { + function clear() { + matrix3.clear( this.buffer ); + this.modified = true; - 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])) - ); - }, + return this; + } - clear: vector.clear, + function clone() { + return new Matrix3( this ); + } - cross: function( v1, v2, result ) { - result = result || Vector3(); + function determinant() { + return matrix3.determinant( this.buffer ); + } - 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 equal( arg ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - return result; - }, + return matrix3.equal( this.buffer, other ); + } - dot: function( v1, v2 ) { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - }, + 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; - equal: vector.equal, + return this; + } - length: vector.length, + function multiply( arg, result ) { + var other; + if( arg instanceof Matrix3 ) { + other = arg.buffer; + } else { + other = arg; + } - multiply: function( v, s, result ) { - result = result || Vector3(); + result = result || this; + matrix3.multiply( this.buffer, other, result.buffer ); + result.modified = true; - return vector.multiply( v, s, result ); - }, + return this; + } - normal: function( v1, v2, result ) { - result = result || Vector3(); + 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 Vector3.cross( v1, v2, result ); - }, + return this; + } - normalize: function( v, result ) { - result = result || Vector3(); - 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 = result || this; + matrix3.subtract( this.buffer, other, result.buffer ); + result.modified = true; - return result; - }, - - set: function( v, x, y, z ) { - v[0] = x; - v[1] = y; - v[2] = z; - }, + return this; + } - subtract: function( v1, v2, result ) { - result = result || Vector3(); + function transpose( result ) { + result = result || this; + matrix3.transpose( this.buffer, result.buffer ); + result.modified = true; - return vector.subtract( v1, v2, result ); - } + 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( vector3, 'x', { - get: function() { - return Vector3( [1, 0, 0] ); - }, - enumerable: true - }); + return Matrix3; - Object.defineProperty( vector3, 'y', { - get: function() { - return Vector3( [0, 1, 0] ); - }, - enumerable: true - }); + }; - Object.defineProperty( vector3, 'z', { - get: function() { - return Vector3( [0, 0, 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; + } - Object.defineProperty( vector3, 'zero', { - get: function() { - return Vector3( [0, 0, 0] ); - }, - enumerable: true - }); + var matrix = new FLOAT_ARRAY_TYPE( 16 ); + for( var i = 0; i < 16; ++ i ) { + matrix[i] = elements[i]; + } - Object.defineProperty( vector3, 'one', { - get: function() { - return Vector3( [1, 1, 1] ); - }, - enumerable: true - }); + return matrix; + }; + M4.prototype = new M(); + M4.prototype.constructor = M4; - return vector3; + 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/vector4',['require','./vector'],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 vector = require( './vector' )( 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]; + + return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; + } - var Vector4 = function() { - if( 0 === arguments.length ) { - return vector.$( 4, [0, 0, 0, 0] ); - } else { - return vector.$( 4, arguments ); - } - }; + function equal( m1, m2, e ) { + e = e || 0.000001; - var vector4 = { - - $: Vector4, + if( m1.length !== m2.length ) { + return false; + } - add: function( v1, v2, result ) { - result = result || Vector4(); + 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; + } - 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 true; + } - return result; - }, + 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; + } - // 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])) - ); - }, + // 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; + } - clear: vector.clear, + function multiplyV3( m, v, result ) { + result = result || new V3(); - // 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]; - }, + 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]; - equal: vector.equal, + return result; + } - length: vector.length, + 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; + } - // Computes v * s - multiply: function( v, s, result ) { - result = result || Vector4(); + 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; + } - return vector.multiply( v, s, 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; + } - // Computes a Vector4 with same direction as v having unit length - normalize: function( v, result ) { - result = result || Vector4(); - var len = vector.length(v); + 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; + } - result[0] = v[0]/len; - result[1] = v[1]/len; - result[2] = v[2]/len; - result[3] = v[3]/len; + 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; - 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 ); - } + }; - } - - Object.defineProperty( vector4, 'x', { - get: function() { - return Vector4( [1, 0, 0, 0] ); - }, - enumerable: true - }); +}); +define('matrix/matrix4',['require','../../lib/lodash','common/not-implemented','matrix/m4','matrix/matrix4-api','matrix/matrix'],function ( require ) { - 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 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]; + } - Object.defineProperty( vector4, 'zero', { - get: function() { - return Vector4( [0, 0, 0, 0] ); - }, - enumerable: true - }); + function getValue( index ) { + return this.buffer[index]; + } - Object.defineProperty( vector4, 'one', { - get: function() { - return Vector4( [1, 1, 1, 1] ); - }, - enumerable: true - }); + 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 ); + } + } - return vector4; + 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 */ - -define('vector/quaternion',['require','./vector4','./vector3'],function ( require ) { + 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(); + } - return function( FLOAT_ARRAY_TYPE ) { + 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 ) + } + }); - var vector4 = require( './vector4' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( './vector3' )( FLOAT_ARRAY_TYPE ); + this._views = []; - var Quaternion = vector4.$; + updateViews.call( this ); - var quaternion = { + this.modified = true; + }; + Matrix4.prototype = new Matrix(); + Matrix4.prototype.constructor = Matrix4; - $: Quaternion, + function add( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - to: { - rpy: function( q, result ) { - var r = result || vector3.$(); - var atan2 = Math.atan2, - asin = Math.asin; + result = result || this; + matrix4.add( this.buffer, other, result.buffer ); + result.modified = true; - 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 this; + } - 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 clear() { + matrix4.clear( this.buffer ); + this.modified = true; - length: vector4.length, + return this; + } - multiply: function( q1, q2, result ) { - var r = result || quaternion.$(); + function clone() { + return new Matrix4( this ); + } - 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 + function determinant() { + return matrix4.determinant( this.buffer ); + } - if( !result ) { - return r; - } - }, + function equal( arg ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - normalize: vector4.normalize + 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; - Object.defineProperty( quaternion, 'identity', { - get: function() { - return Quaternion( [0, 0, 0, 1] ); - }, - enumerable: true - }); + return this; + } - return quaternion; + 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; + } -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + 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; + } -define('matrix/matrix',['require'],function ( require ) { + function subtract( arg, result ) { + var other; + if( arg instanceof Matrix4 ) { + other = arg.buffer; + } else { + other = arg; + } - 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]; - } + result = result || this; + matrix4.subtract( this.buffer, other, result.buffer ); + result.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; + return this; + } - 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 transpose( result ) { + result = result || this; + matrix4.transpose( this.buffer, result.buffer ); + result.modified = true; - return true; - } - - }; - - return matrix; - - }; + return this; + } -}); - -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ - -define('matrix/matrix2',['require','./matrix'],function ( require ) { + _.extend( Matrix4.prototype, { + add: add, + clear: clear, + clone: clone, + determinant: determinant, + equal: equal, + inverse: inverse, + multiply: multiply, + set: set, + subtract: subtract, + transpose: transpose + }); - return function( FLOAT_ARRAY_TYPE ) { + return Matrix4; - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + }; - var Matrix2 = function() { - if( 0 === arguments.length ) { - return matrix.$( 4, [0, 0, - 0, 0] ); - } else { - return matrix.$( 4, arguments ); - } - }; +}); +define('matrix/transform-api',['require','common/not-implemented','matrix/m4','matrix/matrix4-api'],function ( require ) { - var matrix2 = { + return function( FLOAT_ARRAY_TYPE ) { - $: Matrix2, + 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 || 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 compound( transform, t, r, s ) { - 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; - }, + 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 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; - } - } + return transform; + } - 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 set(transform, t, r, s){ + if (transform){ + matrix4.set(transform, matrix4.identity); + } + return compound(transform, t, r, s); + } - return matrix2; + function rotate( v, result ) { + result = result || new M4( matrix4.identity ); - }; + var sinA, + cosA; + var rotation; -}); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + if( 0 !== v[2] ) { + sinA = Math.sin( v[2] ); + cosA = Math.cos( v[2] ); -define('matrix/matrix3',['require','./matrix'],function ( require ) { + rotation = [ cosA, -sinA, 0, 0, + sinA, cosA, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - return function( FLOAT_ARRAY_TYPE ) { + if( 0 !== v[1] ) { + sinA = Math.sin( v[1] ); + cosA = Math.cos( v[1] ); - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); + rotation = [ cosA, 0, sinA, 0, + 0, 1, 0, 0, + -sinA, 0, cosA, 0, + 0, 0, 0, 1 ]; + matrix4.multiply( result, rotation, result ); + } - 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( 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 ); + } - var matrix3 = { - - $: Matrix3, + return result; + } - 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 scale( v, result ) { + result = result || new M4( matrix4.identity ); - 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; - }, + matrix4.multiply( result, [v[0], 0, 0, 0, + 0, v[1], 0, 0, + 0, 0, v[2], 0, + 0, 0, 0, 1], result ); - clear: matrix.clear, + return result; + } - equal: matrix.equal, + function translate( v, result ) { + result = result || new M4( matrix4.identity ); - determinant: function( m ) { + matrix4.multiply( result, [1, 0, 0, v[0], + 0, 1, 0, v[1], + 0, 0, 1, v[2], + 0, 0, 0, 1], result ); - 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; + return result; + } - 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 ); - } + var transform = { + compound: compound, + set: set, + rotate: rotate, + scale: scale, + translate: translate + }; - 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 ); - } + return transform; - 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( !result ) { - return r; - } - }, +}); +define('matrix/t',['require','matrix/m','matrix/m4','matrix/transform-api'],function ( require ) { - transpose: function( m, result ) { - result = result || Matrix3(); + return function( FLOAT_ARRAY_TYPE ) { - 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; - } + var M = require( "matrix/m" ); + var M4 = require( "matrix/m4" )( FLOAT_ARRAY_TYPE ); + var transform = require("matrix/transform-api")( FLOAT_ARRAY_TYPE ); - }; - - 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 - }); + 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 matrix3; + return T; - }; + }; }); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ +define('matrix/transform',['require','common/not-implemented','matrix/m4','matrix/transform-api','matrix/matrix4-api','matrix/matrix4'],function ( require ) { -define('matrix/matrix4',['require','./matrix','../vector/vector3'],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]; + } - return function( FLOAT_ARRAY_TYPE ) { + function getValue( index ) { + return this.buffer[index]; + } - var matrix = require( './matrix' )( FLOAT_ARRAY_TYPE ); - var vector3 = require( '../vector/vector3' )( FLOAT_ARRAY_TYPE ); + function setValue( index, value ) { + this.buffer[index] = value; + this.matrix.modified = true; + } - 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 ); - } - }; + function updateViews() { + var i; + for( i = 0; i < 4; ++ i ) { + this._views[i] = new TransformView( this, this.buffer, + i*4, (i+1)*4 ); + } + } - var matrix4 = { - - $: Matrix4, + var TransformView = function( matrix, buffer, start, end ) { + this.matrix = matrix; + this.buffer = buffer.subarray( start, end ); - 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; - }, + 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 ) + } + }); + }; - clear: matrix.clear, + 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 ); + } - equal: matrix.equal, + 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 ) + } + }); - 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]; + this._views = []; - return result; - }, + updateViews.call( this ); - 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; - }, + this.modified = true; + }; - 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 clone() { + return new Transform( this ); + } - 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; - }, + function equal( arg ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } - 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; - } + return matrix4.equal( this.buffer, other ); + } - }; - 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 multiply( arg, result ) { + var other; + if( arg instanceof Matrix4 || + arg instanceof Transform ) { + other = arg.buffer; + } else { + other = arg; + } - return matrix4; + result = result || this; + matrix4.multiply( this.buffer, other, result.buffer ); + result.modified = true; - }; + return this; + } -}); + function rotate( v, result ) { + var rotation = transform.rotate( v ); -/*jshint white: false, strict: false, plusplus: false, onevar: false, -nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + result = result || this; + matrix4.multiply( this.buffer, rotation, result.buffer ); + result.modified = true; -define('matrix/transform',['require','./matrix4'],function ( require ) { + return this; + } - return function( FLOAT_ARRAY_TYPE ) { + function scale( v, result ) { + var scaled = transform.scale( v ); - var matrix4 = require( './matrix4' )( FLOAT_ARRAY_TYPE ); + result = result || this; + matrix4.multiply( this.buffer, scaled, result.buffer ); + result.modified = true; - var Transform = matrix4.$; - - var transform = { - - $: Transform, + return this; + } - fixed: function( vt, vr, vs ) { - var r = matrix4.identity; + function set( t, r, s ) { + transform.compound( this.buffer, t, r, s ); + this.modified = true; + } - if( vt ) { - transform.translate( vt, r ); - } + function transformDirection( v, result ) { - if( vr ) { - transform.rotate( vr, r ); - } + } - if( vs ) { - transform.scale( vs, r ); - } + function transformPoint( v, result ) { - return r; - }, + } - // 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 translate( v, result ) { + var translation = transform.translate( v ); - 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 ); - } + result = result || this; + matrix4.multiply( this.buffer, translation, result.buffer ); + result.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 ); - } + return this; + } - if( !result ) { - return r; - } - }, + Transform.prototype = { + clone: clone, + equal: equal, + inverseTransformDirection: notImplemented, + inverseTransformPoint: notImplemented, + multiply: multiply, + rotate: rotate, + scale: scale, + set: set, + transformDirection: notImplemented, + transformPoint: notImplemented, + translate: translate + }; - // 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 Transform; - 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 ] +}); +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 ) { - if( result ) { - matrix4.multiply( [ r, matrix4.$( result ) ], result ); - } else { - return r; - } - } + var constants = require( "constants" ); + var equal = require( "equal" ); - }; - - return transform; + 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" ); -/*jshint white: false, strict: false, plusplus: false, onevar: false, - nomen: false */ -/*global define: false, console: false, window: false, setTimeout: false */ + var M2 = require( "matrix/m2" ); + var Matrix2 = require( "matrix/matrix2" ); + var matrix2 = require( "matrix/matrix2-api" ); -define('_math',['require','./lang','./constants','./vector/vector2','./vector/vector3','./vector/vector4','./vector/quaternion','./matrix/matrix2','./matrix/matrix3','./matrix/matrix4','./matrix/transform'],function ( require ) { + var M3 = require( "matrix/m3" ); + var Matrix3 = require( "matrix/matrix3" ); + var matrix3 = require( "matrix/matrix3-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 M4 = require( "matrix/m4" ); + var Matrix4 = require( "matrix/matrix4" ); + var matrix4 = require( "matrix/matrix4-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 T = require( "matrix/t" ); + var Transform = require( "matrix/transform" ); + var transform = require( "matrix/transform-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 - }); - + 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,7 +99613,6 @@ 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._timeStep = 30; // time step, in milliseconds @@ -90462,6 +99669,8 @@ define('src/services/resolver',['require','base/service','core/event','_math','b this.world.SetContactListener( contactListener ); }; + var totalForce = new math.Vector2(); + function resolve() { var component; @@ -90476,13 +99685,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 @@ -90620,6 +99829,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 +99856,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 b2Vector = new Box2D.b2Vec2( 0, 0 ); + + function setAngularVelocity(rotation){ + this.box2dBody.SetAngularVelocity(rotation); + } + + function setLinearVelocity(arg1, arg2) { + var argc = arguments.length; + if( 1 === argc ) { + b2Vector.Set( arg1[0], arg1[1] ); + }else{ + b2Vector.Set( arg1, arg2); + } + this.box2dBody.SetLinearVelocity( b2Vector ); + b2Vector.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 ); + b2Vector.Set( impulse[0], impulse[1] ); + this.box2dBody.ApplyLinearImpulse( b2Vector, this.box2dBody.GetPosition() ); + b2Vector.Set( 0, 0 ); } function onAngularImpulse( event ) { @@ -90661,10 +99888,11 @@ 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 + transform.position = [ position2.get_x(), position2.get_y(), transform.position.buffer[2] ]; + transform.rotation.z = angle2; } function onEntitySpaceChanged( event ) { @@ -90692,7 +99920,8 @@ 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 + 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 ) { @@ -90716,6 +99945,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 +100040,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 +100079,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 +100108,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 +100117,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 +100160,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..d5be028 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; }; + } - extend: function extend( object, extra ) { - for ( var prop in extra ) { - if ( !object.hasOwnProperty( prop ) && extra.hasOwnProperty( prop ) ) { - object[prop] = extra[prop]; - } //if - } //for - } //extend + 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; }; -}); + }); -/*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; + } + + 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(); - return vector; + 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; + + }; - return vector2; +}); +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; + } -}); -/*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]; -define('vector/quaternion',['require','./vector4','./vector3'],function ( require ) { + 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; + + 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; + } + + 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 quaternion; + 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; + } - 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 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; + } - 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; -define('matrix/matrix3',['require','./matrix'],function ( require ) { + 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; - 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 ) { + transform = transform || new M4(matrix4.identity); - 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 || 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 ); - } + 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 - }); + if( 0 !== v[0] ) { + sinA = Math.sin( v[0] ); + cosA = Math.cos( v[0] ); - 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 - }); + 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 matrix3; + 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 ) + } + }); - return matrix4; + this._views = []; + 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,169 @@ 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 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; + } - 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 setRotation( rotation ) { - math.vector3.set( this.rotation, rotation[0], rotation[1], rotation[2] ); - this._cachedIsValid = false; + //TODO: Should produce a unit vector showing the orientation of things in world space + function directionToWorld(){ - return this; } - function setScale( scale ) { - math.vector3.set( this.scale, scale[0], scale[1], scale[2] ); - this._cachedIsValid = false; - - return this; + function pointToWorld(direction, result) { + result = result || new math.V3(); + direction = direction || 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 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 the temp matrix + //Solution grabbed from http://www.macaronikazoo.com/?p=419 + 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]); + } + 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 relativeTo(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 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 + 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, + pointToLocal: pointToLocal, + pointToWorld: pointToWorld, + directionToWorld: undefined, + //Same thing goes for this one. + worldRotation: computeWorldRotation, + relativeTo: relativeTo, + toWorldPoint: toWorldPoint, + 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" } }