├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── app ├── creature.js ├── display.js ├── dom.js ├── main.js ├── terrarium.js └── util.js ├── bower.json ├── dist ├── terra.js └── terra.min.js ├── gulpfile.js ├── lodash_custom ├── lodash.custom.js └── lodash.custom.min.js ├── package.json └── tests └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache 2 | demo 3 | bower_components 4 | node_modules 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 rileyjshaw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | terra 2 | ===== 3 | 4 | JS library for cellular automata and simple biological simulations. Documentation and examples live [here](http://rileyjshaw.com/terra/). 5 | 6 | ## Hacking this library 7 | To build terra on your machine you'll need [Node.js](http://nodejs.org/), [Bower](http://bower.io/), and [gulp](http://gulpjs.com/) installed. Then... 8 | 9 | ```.bash 10 | cd path/to/terra 11 | npm install 12 | bower install 13 | gulp 14 | ``` 15 | 16 | ## Contributing 17 | At this stage **the most important way you can help is to use the library**. The API is in Beta and still flexible. If you discover something that's confusing or hard to work with, document it [here](https://github.com/rileyjshaw/terra/issues). Come up with an idea and try to build it; by using and testing the library you'll find bugs or usability issues that would otherwise go unnoticed. 18 | If you want to make a pull-request on anything labeled 'major', be sure to join the discussion first so we can talk architecture. 19 | If anyone's willing to get the ball rolling on [unit tests](https://github.com/rileyjshaw/terra/issues/16), [you will be my hero](http://youtu.be/koJlIGDImiU). 20 | 21 | 22 | That's all, folks! MIT, remixing strongly encouraged. 23 | -------------------------------------------------------------------------------- /app/creature.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util.js'); 2 | 3 | // abstract factory that adds a superclass of baseCreature 4 | var factory = (function () { 5 | function baseCreature() { 6 | this.age = -1; 7 | } 8 | function baseCA() { 9 | this.age = -1; 10 | } 11 | 12 | baseCreature.prototype.initialEnergy = 50; 13 | baseCreature.prototype.maxEnergy = 100; 14 | baseCreature.prototype.efficiency = 0.7; 15 | baseCreature.prototype.size = 50; 16 | baseCreature.prototype.actionRadius = 1; 17 | baseCreature.prototype.sustainability = 2; 18 | // used as percentages of maxEnergy 19 | baseCreature.prototype.reproduceLv = 0.70; 20 | baseCreature.prototype.moveLv = 0; 21 | 22 | baseCreature.prototype.boundEnergy = function() { 23 | if (this.energy > this.maxEnergy) 24 | this.energy = this.maxEnergy; 25 | }; 26 | 27 | baseCreature.prototype.isDead = function() { 28 | return this.energy <= 0; 29 | }; 30 | 31 | baseCreature.prototype.reproduce = function (neighbors) { 32 | var spots = _.filter(neighbors, function (spot) { 33 | return !spot.creature; 34 | }); 35 | 36 | if (spots.length) { 37 | var step = spots[_.random(spots.length - 1)]; 38 | var coords = step.coords; 39 | var creature = factory.make(this.type); 40 | 41 | var successFn = (function () { 42 | this.energy -= this.initialEnergy; 43 | return true; 44 | }).bind(this); 45 | var failureFn = this.wait; 46 | 47 | return { 48 | x: coords.x, 49 | y: coords.y, 50 | creature: creature, 51 | successFn: successFn, 52 | failureFn: failureFn 53 | }; 54 | } else return false; 55 | }; 56 | 57 | baseCreature.prototype.move = function (neighbors) { 58 | var creature = this; 59 | 60 | // first, look for creatures to eat 61 | var spots = _.filter(neighbors, (function (spot) { 62 | return spot.creature.size < this.size; 63 | }).bind(this)); 64 | 65 | // if there's not enough food, try to move 66 | if (spots.length < this.sustainability) { 67 | spots = _.filter(neighbors, function (spot) { 68 | return !spot.creature; 69 | }); 70 | } 71 | 72 | // if we've got a spot to move to... 73 | if (spots.length) { 74 | // ...pick one 75 | var step = spots[_.random(spots.length - 1)]; 76 | 77 | var coords = step.coords; 78 | 79 | var successFn = (function () { 80 | var foodEnergy = step.creature.energy * this.efficiency; 81 | // add foodEnergy if eating, subtract 10 if moving 82 | this.energy = this.energy + (foodEnergy || -10); 83 | // clear the original location 84 | return false; 85 | }).bind(this); 86 | 87 | return { 88 | x: coords.x, 89 | y: coords.y, 90 | creature: creature, 91 | successFn: successFn 92 | }; 93 | } else return false; 94 | }; 95 | 96 | baseCreature.prototype.wait = function () { 97 | this.energy -= 5; 98 | return true; 99 | }; 100 | 101 | baseCreature.prototype.process = function (neighbors, x, y) { 102 | var step = {}; 103 | var maxEnergy = this.maxEnergy; 104 | 105 | if (this.energy > maxEnergy * this.reproduceLv && this.reproduce) { 106 | step = this.reproduce(neighbors); 107 | } else if (this.energy > maxEnergy * this.moveLv && this.move) { 108 | step = this.move(neighbors); 109 | } 110 | 111 | var creature = step.creature; 112 | 113 | if (creature) { 114 | creature.successFn = step.successFn || creature.wait; 115 | creature.failureFn = step.failureFn || creature.wait; 116 | 117 | return { 118 | x: step.x, 119 | y: step.y, 120 | creature: creature, 121 | observed: true 122 | }; 123 | } else return this.energy !== this.maxEnergy; 124 | }; 125 | 126 | baseCA.prototype.actionRadius = 1; 127 | baseCA.prototype.boundEnergy = function () {}; 128 | baseCA.prototype.isDead = function () { return false; }; 129 | baseCA.prototype.process = function (neighbors, x, y) {}; 130 | baseCA.prototype.wait = function () {}; 131 | 132 | // Storage for our creature types 133 | var types = {}; 134 | 135 | return { 136 | make: function (type, options) { 137 | var Creature = types[type]; 138 | return (Creature ? new Creature(options) : false); 139 | }, 140 | 141 | registerCreature: function (options, init) { 142 | // required attributes 143 | var type = options.type; 144 | // only register classes that fulfill the creature contract 145 | if (typeof type === 'string' && typeof types[type] === 'undefined') { 146 | // set the constructor, including init if it's defined 147 | if (typeof init === 'function') { 148 | types[type] = function () { 149 | this.energy = this.initialEnergy; 150 | init.call(this); 151 | }; 152 | } else { 153 | types[type] = function () { 154 | this.energy = this.initialEnergy; 155 | }; 156 | } 157 | 158 | var color = options.color || options.colour; 159 | // set the color randomly if none is provided 160 | if (typeof color !== 'object' || color.length !== 3) { 161 | options.color = [_.random(255), _.random(255), _.random(255)]; 162 | } 163 | 164 | types[type].prototype = new baseCreature(); 165 | types[type].prototype.constructor = types[type]; 166 | 167 | _.each(options, function(value, key) { 168 | types[type].prototype[key] = value; 169 | }); 170 | 171 | types[type].prototype.successFn = types[type].prototype.wait; 172 | types[type].prototype.failureFn = types[type].prototype.wait; 173 | types[type].prototype.energy = options.initialEnergy; 174 | 175 | return true; 176 | } else return false; 177 | }, 178 | 179 | registerCA: function (options, init) { 180 | // required attributes 181 | var type = options.type; 182 | // only register classes that fulfill the creature contract 183 | if (typeof type === 'string' && typeof types[type] === 'undefined') { 184 | // set the constructor, including init if it's defined 185 | types[type] = typeof init === 'function' ? 186 | function () { init.call(this); } : 187 | function () {}; 188 | 189 | var color = options.color = options.color || options.colour; 190 | // set the color randomly if none is provided 191 | if (typeof color !== 'object' || color.length !== 3) { 192 | options.color = [_.random(255), _.random(255), _.random(255)]; 193 | } 194 | 195 | options.colorFn = options.colorFn || options.colourFn; 196 | 197 | types[type].prototype = new baseCA(); 198 | types[type].prototype.constructor = types[type]; 199 | 200 | _.each(options, function(value, key) { 201 | types[type].prototype[key] = value; 202 | }); 203 | 204 | return true; 205 | } else return false; 206 | } 207 | }; 208 | })(); 209 | 210 | module.exports = factory; 211 | -------------------------------------------------------------------------------- /app/display.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util.js'); 2 | 3 | module.exports = function (canvas, grid, cellSize, trails, background) { 4 | var ctx = canvas.getContext('2d'); 5 | if (trails && background) { 6 | ctx.fillStyle = 'rgba(' + background + ',' + (1 - trails) + ')'; 7 | ctx.fillRect(0, 0, canvas.width, canvas.height); 8 | } else if (trails) { 9 | throw "Background must also be set for trails"; 10 | } else ctx.clearRect(0, 0, canvas.width, canvas.height); 11 | 12 | _.each(grid, function (column, x) { 13 | _.each(column, function (creature, y) { 14 | if (creature) { 15 | var color = creature.colorFn ? 16 | creature.colorFn() : 17 | creature.color + ',' + creature.energy / creature.maxEnergy; 18 | 19 | ctx.fillStyle = 'rgba(' + color + ')'; 20 | 21 | if (creature.character) { 22 | ctx.fillText(creature.character, x * cellSize, y * cellSize + cellSize); 23 | } else { 24 | ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); 25 | } 26 | } 27 | }); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /app/dom.js: -------------------------------------------------------------------------------- 1 | // Creates an HD canvas element on page and 2 | // returns a reference to the element 3 | var createCanvasElement = function (width, height, cellSize, id, insertAfter, background) { 4 | width *= cellSize; 5 | height *= cellSize; 6 | 7 | // Creates a scaled-up canvas based on the device's 8 | // resolution, then displays it properly using styles 9 | function createHDCanvas () { 10 | var canvas = document.createElement('canvas'); 11 | var ctx = canvas.getContext('2d'); 12 | 13 | // Creates a dummy canvas to test device's pixel ratio 14 | var ratio = (function () { 15 | var ctx = document.createElement('canvas').getContext('2d'); 16 | var dpr = window.devicePixelRatio || 1; 17 | var bsr = ctx.webkitBackingStorePixelRatio || 18 | ctx.mozBackingStorePixelRatio || 19 | ctx.msBackingStorePixelRatio || 20 | ctx.oBackingStorePixelRatio || 21 | ctx.backingStorePixelRatio || 1; 22 | return dpr / bsr; 23 | })(); 24 | 25 | canvas.width = width * ratio; 26 | canvas.height = height * ratio; 27 | canvas.style.width = width + 'px'; 28 | canvas.style.height = height + 'px'; 29 | ctx.scale(ratio, ratio); 30 | ctx.font = 'bold ' + cellSize + 'px Arial'; 31 | 32 | if (id) canvas.id = id; 33 | if (background) canvas.style.background = 'rgb(' + background + ')'; 34 | 35 | return canvas; 36 | } 37 | 38 | var canvas = createHDCanvas(); 39 | 40 | if (insertAfter) insertAfter.parentNode.insertBefore(canvas, insertAfter.nextSibling); 41 | else document.body.appendChild(canvas); 42 | 43 | return canvas; 44 | }; 45 | 46 | module.exports = { 47 | createCanvasElement: createCanvasElement 48 | }; 49 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | var Terrarium = require('./terrarium.js'); 2 | var factory = require('./creature.js'); 3 | 4 | module.exports = { 5 | Terrarium: Terrarium, 6 | registerCreature: factory.registerCreature, 7 | registerCA: factory.registerCA 8 | }; 9 | -------------------------------------------------------------------------------- /app/terrarium.js: -------------------------------------------------------------------------------- 1 | var _ = require('./util'); 2 | var factory = require('./creature.js'); 3 | var display = require('./display.js'); 4 | var dom = require('./dom.js'); 5 | 6 | /** 7 | * Terrarium constructor function 8 | * @param {int} width number of cells in the x-direction 9 | * @param {int} height number of cells in the y-direction 10 | * @param {object} options 11 | * @param {string} id id assigned to the generated canvas 12 | * @param {int} cellSize pixel width of each cell (default 10) 13 | * @param {string} insertAfter id of the element to insert the canvas after 14 | * @param {float} trails a number from [0, 1] indicating whether trails should 15 | * be drawn (0 = no trails, 1 = neverending trails) 16 | * "background" option is required if trails is set 17 | * @param {array} background an RGB triplet for the canvas' background 18 | */ 19 | function Terrarium (width, height, options) { 20 | var cellSize, neighborhood; 21 | 22 | // cast width and height to integers 23 | width = Math.ceil(width); 24 | height = Math.ceil(height); 25 | 26 | // set default options 27 | options = options || {}; 28 | cellSize = options.cellSize || 10; 29 | neighborhood = options.neighborhood || options.neighbourhood; 30 | if (typeof neighborhood === 'string') neighborhood = neighborhood.toLowerCase(); 31 | 32 | this.width = width; 33 | this.height = height; 34 | this.cellSize = cellSize; 35 | this.trails = options.trails; 36 | this.background = options.background; 37 | this.canvas = dom.createCanvasElement(width, height, cellSize, options.id, options.insertAfter, this.background); 38 | this.grid = []; 39 | this.nextFrame = false; 40 | this.hasChanged = false; 41 | this.getNeighborCoords = _.getNeighborCoordsFn(width, height, neighborhood === 'vonneumann', options.periodic); 42 | } 43 | 44 | /** 45 | * Create a grid and fill it by using a function, 2-d array, or uniform type 46 | * @param {*} content if function, fill grid according to fn(x, y) 47 | * if array, fill grid cells with the corresponding creatureType 48 | * if string, fill grid with that creatureType 49 | * otherwise, create empty grid 50 | * @return {grid} a grid adhering to the above rules 51 | */ 52 | Terrarium.prototype.makeGrid = function (content) { 53 | var grid = [], type = typeof content; 54 | for (var x = 0, _w = this.width; x < _w; x++) { 55 | grid.push([]); 56 | for (var y = 0, _h = this.height; y < _h; y++) { 57 | grid[x].push(factory.make( 58 | type === 'function' ? content(x, y) : 59 | type === 'object' && content.length ? (content[y] || [])[x] : 60 | type === 'string' ? content : 61 | undefined 62 | )); 63 | } 64 | } return grid; 65 | }; 66 | 67 | /** 68 | * Create a grid and fill it randomly with a set creature distribution 69 | * @param {array} distribution an array of arrays of the form [string 'creatureName', float fillPercent] 70 | */ 71 | Terrarium.prototype.makeGridWithDistribution = function (distribution) { 72 | var current, rand = 0, grid = []; 73 | for (var x = 0, _w = this.width; x < _w; x++) { 74 | grid.push([]); 75 | for (var y = 0, _h = this.height; y < _h; y++) { 76 | grid[x].push(factory.make(_.pickRandomWeighted(distribution))); 77 | } 78 | } return grid; 79 | }; 80 | 81 | /** 82 | * Returns the next step of the simulation 83 | * @param {} steps the number of steps to run through before returning 84 | * @return {grid} a new grid after || 1 steps 85 | */ 86 | Terrarium.prototype.step = function (steps) { 87 | function copyAndRemoveInner (origCreature) { 88 | if (origCreature) { 89 | var copy = _.assign(new (origCreature.constructor)(), origCreature); 90 | var dead = copy && copy.isDead(); 91 | if (dead && !self.hasChanged) self.hasChanged = true; 92 | copy.age++; 93 | 94 | return !dead ? copy : false; 95 | } else return false; 96 | } 97 | 98 | function copyAndRemove (origCols) { 99 | return _.map(origCols, copyAndRemoveInner); 100 | } 101 | 102 | // TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner 103 | function zipCoordsWithNeighbors (coords) { 104 | return { 105 | coords: coords, 106 | creature: oldGrid[coords.x][coords.y] 107 | }; 108 | } 109 | 110 | function processLoser (loser) { 111 | var loserCreature = loser.creature; 112 | if (loserCreature) { 113 | loserCreature.failureFn(); 114 | loserCreature.boundEnergy(); 115 | } else { 116 | loser.wait(); 117 | loser.boundEnergy(); 118 | } 119 | } 120 | 121 | function processCreaturesInner (creature, x, y) { 122 | if (creature) { 123 | var neighbors = _.map( 124 | self.getNeighborCoords(x, y, creature.actionRadius), 125 | zipCoordsWithNeighbors 126 | ); 127 | var result = creature.process(neighbors, x, y); 128 | if (typeof result === 'object') { 129 | var eigenColumn = eigenGrid[result.x]; 130 | var returnedCreature = result.creature; 131 | var returnedY = result.y; 132 | 133 | if (!eigenColumn[returnedY]) eigenColumn[returnedY] = []; 134 | 135 | eigenColumn[returnedY].push({ 136 | x: x, 137 | y: y, 138 | creature: returnedCreature 139 | }); 140 | if (!self.hasChanged && result.observed) self.hasChanged = true; 141 | } else { 142 | if (result && !self.hasChanged) self.hasChanged = true; 143 | processLoser(creature); 144 | } 145 | } 146 | } 147 | 148 | function processCreatures (column, x) { 149 | _.each(column, function (creature, y) { processCreaturesInner(creature, x, y); }); 150 | } 151 | 152 | function pickWinnerInner (superposition, x, y) { 153 | if (superposition) { 154 | var winner = superposition.splice(_.random(superposition.length - 1), 1)[0]; 155 | var winnerCreature = winner.creature; 156 | 157 | // clear the original creature's square if successFn returns false 158 | if (!winnerCreature.successFn()) { 159 | newGrid[winner.x][winner.y] = false; 160 | } 161 | // TODO: so many calls to this. Can we just run it once at the start of a step? 162 | winnerCreature.boundEnergy(); 163 | 164 | // put the winner in its rightful place 165 | newGrid[x][y] = winnerCreature; 166 | 167 | // ...and call wait() on the losers. We can do this without 168 | // affecting temporal consistency because all callbacks have 169 | // already been created with prior conditions 170 | _.each(superposition, processLoser); 171 | } 172 | } 173 | 174 | function pickWinner (column, x) { 175 | _.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); }); 176 | } 177 | 178 | var self = this; 179 | var gridWidth = this.width; 180 | var gridHeight = this.height; 181 | var oldGrid = this.grid, newGrid, eigenGrid; 182 | 183 | if (typeof steps !== 'number') steps = 1; 184 | 185 | while (steps--) { 186 | this.hasChanged = false; 187 | 188 | oldGrid = newGrid ? _.clone(newGrid) : this.grid; 189 | 190 | // copy the old grid & remove dead creatures 191 | newGrid = _.map(oldGrid, copyAndRemove); 192 | 193 | // create an empty grid to hold creatures competing for the same square 194 | eigenGrid = this.makeGrid(); 195 | 196 | // Add each creature's intended destination to the eigenGrid 197 | _.each(newGrid, processCreatures); 198 | 199 | // Choose a winner from each of the eigenGrid's superpositions 200 | _.each(eigenGrid, pickWinner); 201 | 202 | if (!this.hasChanged) return false; 203 | } 204 | 205 | return newGrid; 206 | }; 207 | 208 | /** 209 | * Updates the canvas to reflect the current grid 210 | */ 211 | Terrarium.prototype.draw = function () { 212 | display(this.canvas, this.grid, this.cellSize, this.trails, this.background); 213 | }; 214 | 215 | /** 216 | * Starts animating the simulation. Can be called with only a function. 217 | * @param {int} steps the simulation will stop after steps if specified 218 | * @param {Function} fn called as a callback once the animation finishes 219 | */ 220 | Terrarium.prototype.animate = function (steps, fn) { 221 | function tick () { 222 | var grid = self.step(); 223 | if (grid) { 224 | self.grid = grid; 225 | self.draw(); 226 | if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick); 227 | } // if grid hasn't changed || reached last step 228 | self.nextFrame = false; 229 | if (fn) fn(); 230 | } 231 | 232 | if (typeof steps === 'function') { 233 | fn = steps; 234 | steps = null; 235 | } 236 | 237 | if (!this.nextFrame) { 238 | var i = 0; 239 | var self = this; 240 | self.nextFrame = requestAnimationFrame(tick); 241 | } 242 | }; 243 | 244 | /** 245 | * Stops a currently running animation 246 | */ 247 | Terrarium.prototype.stop = function () { 248 | cancelAnimationFrame(this.nextFrame); 249 | this.nextFrame = false; 250 | }; 251 | 252 | /** 253 | * Stops any currently running animation and cleans up the DOM 254 | */ 255 | Terrarium.prototype.destroy = function () { 256 | var canvas = this.canvas; 257 | this.stop(); 258 | canvas.parentNode.removeChild(canvas); 259 | }; 260 | 261 | module.exports = Terrarium; 262 | -------------------------------------------------------------------------------- /app/util.js: -------------------------------------------------------------------------------- 1 | // Seed Math.random() with seedrandom 2 | require('../bower_components/seedrandom/seedrandom.js')('terra :)', {global: true}); 3 | 4 | // an extended custom build of lodash, generated with: 5 | // lodash exports=commonjs include=assign,clone,filter,each,map,random,reduce,some 6 | var _ = require('../lodash_custom/lodash.custom.min.js')._; 7 | 8 | /** 9 | * Takes a cell and returns the coordinates of its neighbors 10 | * @param {int} x0 - x position of cell 11 | * @param {int} y0 - y position of cell 12 | * @param {int} xMax - maximum x index i.e. grid width 13 | * @param {int} yMax - maximum x index i.e. grid height 14 | * @param {int} radius - (default = 1) neighbor radius 15 | * @return {array} - an array of [x, y] pairs of the neighboring cells 16 | */ 17 | _.getNeighborCoordsFn = function (xMax, yMax, vonNeumann, periodic) { 18 | if (periodic) { 19 | if (vonNeumann) { 20 | // periodic von neumann 21 | return function (x0, y0, radius) { 22 | var coords = [], x, rX, y, rY, rYMax; 23 | 24 | for (rX = -radius; rX <= radius; ++rX) { 25 | rYMax = radius - Math.abs(rX); 26 | for (rY = -rYMax; rY <= rYMax; ++rY) { 27 | x = ((rX + x0) % xMax + xMax) % xMax; 28 | y = ((rY + y0) % yMax + yMax) % yMax; 29 | if (x !== x0 || y !== y0) { 30 | coords.push({ 31 | x: x, 32 | y: y 33 | }); 34 | } 35 | } 36 | } 37 | 38 | return coords; 39 | }; 40 | } 41 | else { 42 | // periodic moore 43 | return function (x0, y0, radius) { 44 | var coords = [], x, xLo, xHi, y, yLo, yHi; 45 | 46 | xLo = x0 - radius; 47 | yLo = y0 - radius; 48 | xHi = x0 + radius; 49 | yHi = y0 + radius; 50 | 51 | for (x = xLo; x <= xHi; ++x) { 52 | for (y = yLo; y <= yHi; ++y) { 53 | if (x !== x0 || y !== y0) { 54 | coords.push({ 55 | x: (x % xMax + xMax) % xMax, 56 | y: (y % yMax + yMax) % yMax 57 | }); 58 | } 59 | } 60 | } 61 | 62 | return coords; 63 | }; 64 | } 65 | } else { 66 | // non-periodic, need to restrict to within [0, max) 67 | xMax -= 1; 68 | yMax -= 1; 69 | 70 | if (vonNeumann) { 71 | //non-periodic von-neumann 72 | return function (x0, y0, radius) { 73 | var coords = [], x, rX, y, rY, rYMax; 74 | 75 | for (rX = -radius; rX <= radius; ++rX) { 76 | rYMax = radius - Math.abs(rX); 77 | for (rY = -rYMax; rY <= rYMax; ++rY) { 78 | x = rX + x0; 79 | y = rY + y0; 80 | if (x >= 0 && y >=0 && x <= xMax && y <= yMax && (x !== x0 || y !== y0)) { 81 | coords.push({ 82 | x: x, 83 | y: y 84 | }); 85 | } 86 | } 87 | } 88 | 89 | return coords; 90 | }; 91 | } 92 | else { 93 | // non-periodic moore 94 | return function (x0, y0, radius) { 95 | var coords = [], x, xLo, xHi, y, yLo, yHi; 96 | 97 | xLo = Math.max(0, x0 - radius); 98 | yLo = Math.max(0, y0 - radius); 99 | xHi = Math.min(x0 + radius, xMax); 100 | yHi = Math.min(y0 + radius, yMax); 101 | 102 | for (x = xLo; x <= xHi; ++x) 103 | for (y = yLo; y <= yHi; ++y) 104 | if (x !== x0 || y !== y0) 105 | coords.push({ x: x, y: y }); 106 | 107 | return coords; 108 | }; 109 | } 110 | } 111 | }; 112 | 113 | _.pickRandomWeighted = function (weightedArrays) { 114 | var sum = 0, rand = _.random(100, true); 115 | var cur, i; 116 | for (i = 0, _len = weightedArrays.length; i < _len; i++) { 117 | cur = weightedArrays[i]; 118 | sum += cur[1]; 119 | if (sum > rand) return cur[0]; 120 | } return false; 121 | }; 122 | 123 | /** 124 | * CommonJS exports 125 | * @type {Object} 126 | */ 127 | module.exports = _; 128 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terra", 3 | "version": "1.5.2-beta", 4 | "homepage": "https://github.com/rileyjshaw/terra", 5 | "authors": [ 6 | "rileyjshaw " 7 | ], 8 | "description": "A JavaScript library for simple biological simulations and cellular automata.", 9 | "main": "dist/terra.min.js", 10 | "moduleType": [ 11 | "node" 12 | ], 13 | "keywords": [ 14 | "terrarium", 15 | "sumulator", 16 | "cellular", 17 | "automata", 18 | "biology" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "app", 24 | "bower_components", 25 | "demo", 26 | "lodash_custom", 27 | "node_modules", 28 | "tests", 29 | "gulpfile.js" 30 | ], 31 | "dependencies": { 32 | "seedrandom": "~2.3.6" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dist/terra.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.terra=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this.maxEnergy) 35 | this.energy = this.maxEnergy; 36 | }; 37 | 38 | baseCreature.prototype.isDead = function() { 39 | return this.energy <= 0; 40 | }; 41 | 42 | baseCreature.prototype.reproduce = function (neighbors) { 43 | var spots = _.filter(neighbors, function (spot) { 44 | return !spot.creature; 45 | }); 46 | 47 | if (spots.length) { 48 | var step = spots[_.random(spots.length - 1)]; 49 | var coords = step.coords; 50 | var creature = factory.make(this.type); 51 | 52 | var successFn = (function () { 53 | this.energy -= this.initialEnergy; 54 | return true; 55 | }).bind(this); 56 | var failureFn = this.wait; 57 | 58 | return { 59 | x: coords.x, 60 | y: coords.y, 61 | creature: creature, 62 | successFn: successFn, 63 | failureFn: failureFn 64 | }; 65 | } else return false; 66 | }; 67 | 68 | baseCreature.prototype.move = function (neighbors) { 69 | var creature = this; 70 | 71 | // first, look for creatures to eat 72 | var spots = _.filter(neighbors, (function (spot) { 73 | return spot.creature.size < this.size; 74 | }).bind(this)); 75 | 76 | // if there's not enough food, try to move 77 | if (spots.length < this.sustainability) { 78 | spots = _.filter(neighbors, function (spot) { 79 | return !spot.creature; 80 | }); 81 | } 82 | 83 | // if we've got a spot to move to... 84 | if (spots.length) { 85 | // ...pick one 86 | var step = spots[_.random(spots.length - 1)]; 87 | 88 | var coords = step.coords; 89 | 90 | var successFn = (function () { 91 | var foodEnergy = step.creature.energy * this.efficiency; 92 | // add foodEnergy if eating, subtract 10 if moving 93 | this.energy = this.energy + (foodEnergy || -10); 94 | // clear the original location 95 | return false; 96 | }).bind(this); 97 | 98 | return { 99 | x: coords.x, 100 | y: coords.y, 101 | creature: creature, 102 | successFn: successFn 103 | }; 104 | } else return false; 105 | }; 106 | 107 | baseCreature.prototype.wait = function () { 108 | this.energy -= 5; 109 | return true; 110 | }; 111 | 112 | baseCreature.prototype.process = function (neighbors, x, y) { 113 | var step = {}; 114 | var maxEnergy = this.maxEnergy; 115 | 116 | if (this.energy > maxEnergy * this.reproduceLv && this.reproduce) { 117 | step = this.reproduce(neighbors); 118 | } else if (this.energy > maxEnergy * this.moveLv && this.move) { 119 | step = this.move(neighbors); 120 | } 121 | 122 | var creature = step.creature; 123 | 124 | if (creature) { 125 | creature.successFn = step.successFn || creature.wait; 126 | creature.failureFn = step.failureFn || creature.wait; 127 | 128 | return { 129 | x: step.x, 130 | y: step.y, 131 | creature: creature, 132 | observed: true 133 | }; 134 | } else return this.energy !== this.maxEnergy; 135 | }; 136 | 137 | baseCA.prototype.actionRadius = 1; 138 | baseCA.prototype.boundEnergy = function () {}; 139 | baseCA.prototype.isDead = function () { return false; }; 140 | baseCA.prototype.process = function (neighbors, x, y) {}; 141 | baseCA.prototype.wait = function () {}; 142 | 143 | // Storage for our creature types 144 | var types = {}; 145 | 146 | return { 147 | make: function (type, options) { 148 | var Creature = types[type]; 149 | return (Creature ? new Creature(options) : false); 150 | }, 151 | 152 | registerCreature: function (options, init) { 153 | // required attributes 154 | var type = options.type; 155 | // only register classes that fulfill the creature contract 156 | if (typeof type === 'string' && typeof types[type] === 'undefined') { 157 | // set the constructor, including init if it's defined 158 | if (typeof init === 'function') { 159 | types[type] = function () { 160 | this.energy = this.initialEnergy; 161 | init.call(this); 162 | }; 163 | } else { 164 | types[type] = function () { 165 | this.energy = this.initialEnergy; 166 | }; 167 | } 168 | 169 | var color = options.color || options.colour; 170 | // set the color randomly if none is provided 171 | if (typeof color !== 'object' || color.length !== 3) { 172 | options.color = [_.random(255), _.random(255), _.random(255)]; 173 | } 174 | 175 | types[type].prototype = new baseCreature(); 176 | types[type].prototype.constructor = types[type]; 177 | 178 | _.each(options, function(value, key) { 179 | types[type].prototype[key] = value; 180 | }); 181 | 182 | types[type].prototype.successFn = types[type].prototype.wait; 183 | types[type].prototype.failureFn = types[type].prototype.wait; 184 | types[type].prototype.energy = options.initialEnergy; 185 | 186 | return true; 187 | } else return false; 188 | }, 189 | 190 | registerCA: function (options, init) { 191 | // required attributes 192 | var type = options.type; 193 | // only register classes that fulfill the creature contract 194 | if (typeof type === 'string' && typeof types[type] === 'undefined') { 195 | // set the constructor, including init if it's defined 196 | types[type] = typeof init === 'function' ? 197 | function () { init.call(this); } : 198 | function () {}; 199 | 200 | var color = options.color = options.color || options.colour; 201 | // set the color randomly if none is provided 202 | if (typeof color !== 'object' || color.length !== 3) { 203 | options.color = [_.random(255), _.random(255), _.random(255)]; 204 | } 205 | 206 | options.colorFn = options.colorFn || options.colourFn; 207 | 208 | types[type].prototype = new baseCA(); 209 | types[type].prototype.constructor = types[type]; 210 | 211 | _.each(options, function(value, key) { 212 | types[type].prototype[key] = value; 213 | }); 214 | 215 | return true; 216 | } else return false; 217 | } 218 | }; 219 | })(); 220 | 221 | module.exports = factory; 222 | 223 | },{"./util.js":6}],3:[function(require,module,exports){ 224 | var _ = require('./util.js'); 225 | 226 | module.exports = function (canvas, grid, cellSize, trails, background) { 227 | var ctx = canvas.getContext('2d'); 228 | if (trails && background) { 229 | ctx.fillStyle = 'rgba(' + background + ',' + (1 - trails) + ')'; 230 | ctx.fillRect(0, 0, canvas.width, canvas.height); 231 | } else if (trails) { 232 | throw "Background must also be set for trails"; 233 | } else ctx.clearRect(0, 0, canvas.width, canvas.height); 234 | 235 | _.each(grid, function (column, x) { 236 | _.each(column, function (creature, y) { 237 | if (creature) { 238 | var color = creature.colorFn ? 239 | creature.colorFn() : 240 | creature.color + ',' + creature.energy / creature.maxEnergy; 241 | 242 | ctx.fillStyle = 'rgba(' + color + ')'; 243 | 244 | if (creature.character) { 245 | ctx.fillText(creature.character, x * cellSize, y * cellSize + cellSize); 246 | } else { 247 | ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); 248 | } 249 | } 250 | }); 251 | }); 252 | }; 253 | 254 | },{"./util.js":6}],4:[function(require,module,exports){ 255 | // Creates an HD canvas element on page and 256 | // returns a reference to the element 257 | var createCanvasElement = function (width, height, cellSize, id, insertAfter, background) { 258 | width *= cellSize; 259 | height *= cellSize; 260 | 261 | // Creates a scaled-up canvas based on the device's 262 | // resolution, then displays it properly using styles 263 | function createHDCanvas () { 264 | var canvas = document.createElement('canvas'); 265 | var ctx = canvas.getContext('2d'); 266 | 267 | // Creates a dummy canvas to test device's pixel ratio 268 | var ratio = (function () { 269 | var ctx = document.createElement('canvas').getContext('2d'); 270 | var dpr = window.devicePixelRatio || 1; 271 | var bsr = ctx.webkitBackingStorePixelRatio || 272 | ctx.mozBackingStorePixelRatio || 273 | ctx.msBackingStorePixelRatio || 274 | ctx.oBackingStorePixelRatio || 275 | ctx.backingStorePixelRatio || 1; 276 | return dpr / bsr; 277 | })(); 278 | 279 | canvas.width = width * ratio; 280 | canvas.height = height * ratio; 281 | canvas.style.width = width + 'px'; 282 | canvas.style.height = height + 'px'; 283 | ctx.scale(ratio, ratio); 284 | ctx.font = 'bold ' + cellSize + 'px Arial'; 285 | 286 | if (id) canvas.id = id; 287 | if (background) canvas.style.background = 'rgb(' + background + ')'; 288 | 289 | return canvas; 290 | } 291 | 292 | var canvas = createHDCanvas(); 293 | 294 | if (insertAfter) insertAfter.parentNode.insertBefore(canvas, insertAfter.nextSibling); 295 | else document.body.appendChild(canvas); 296 | 297 | return canvas; 298 | }; 299 | 300 | module.exports = { 301 | createCanvasElement: createCanvasElement 302 | }; 303 | 304 | },{}],5:[function(require,module,exports){ 305 | var _ = require('./util'); 306 | var factory = require('./creature.js'); 307 | var display = require('./display.js'); 308 | var dom = require('./dom.js'); 309 | 310 | /** 311 | * Terrarium constructor function 312 | * @param {int} width number of cells in the x-direction 313 | * @param {int} height number of cells in the y-direction 314 | * @param {object} options 315 | * @param {string} id id assigned to the generated canvas 316 | * @param {int} cellSize pixel width of each cell (default 10) 317 | * @param {string} insertAfter id of the element to insert the canvas after 318 | * @param {float} trails a number from [0, 1] indicating whether trails should 319 | * be drawn (0 = no trails, 1 = neverending trails) 320 | * "background" option is required if trails is set 321 | * @param {array} background an RGB triplet for the canvas' background 322 | */ 323 | function Terrarium (width, height, options) { 324 | var cellSize, neighborhood; 325 | 326 | // parse width and height as integers 327 | width = Math.ceil(width); 328 | height = Math.ceil(height); 329 | 330 | // set default options 331 | options = options || {}; 332 | cellSize = options.cellSize || 10; 333 | neighborhood = options.neighborhood || options.neighbourhood; 334 | if (typeof neighborhood === 'string') neighborhood = neighborhood.toLowerCase(); 335 | 336 | this.width = width; 337 | this.height = height; 338 | this.cellSize = cellSize; 339 | this.trails = options.trails; 340 | this.background = options.background; 341 | this.canvas = dom.createCanvasElement(width, height, cellSize, options.id, options.insertAfter, this.background); 342 | this.grid = []; 343 | this.nextFrame = false; 344 | this.hasChanged = false; 345 | this.getNeighborCoords = _.getNeighborCoordsFn(width, height, neighborhood === 'vonneumann', options.periodic); 346 | } 347 | 348 | /** 349 | * Create a grid and fill it by using a function, 2-d array, or uniform type 350 | * @param {*} content if function, fill grid according to fn(x, y) 351 | * if array, fill grid cells with the corresponding creatureType 352 | * if string, fill grid with that creatureType 353 | * otherwise, create empty grid 354 | * @return {grid} a grid adhering to the above rules 355 | */ 356 | Terrarium.prototype.makeGrid = function (content) { 357 | var grid = [], type = typeof content; 358 | for (var x = 0, _w = this.width; x < _w; x++) { 359 | grid.push([]); 360 | for (var y = 0, _h = this.height; y < _h; y++) { 361 | grid[x].push(factory.make( 362 | type === 'function' ? content(x, y) : 363 | type === 'object' && content.length ? (content[y] || [])[x] : 364 | type === 'string' ? content : 365 | undefined 366 | )); 367 | } 368 | } return grid; 369 | }; 370 | 371 | /** 372 | * Create a grid and fill it randomly with a set creature distribution 373 | * @param {array} distribution an array of arrays of the form [string 'creatureName', float fillPercent] 374 | */ 375 | Terrarium.prototype.makeGridWithDistribution = function (distribution) { 376 | var current, rand = 0, grid = []; 377 | for (var x = 0, _w = this.width; x < _w; x++) { 378 | grid.push([]); 379 | for (var y = 0, _h = this.height; y < _h; y++) { 380 | grid[x].push(factory.make(_.pickRandomWeighted(distribution))); 381 | } 382 | } return grid; 383 | }; 384 | 385 | /** 386 | * Returns the next step of the simulation 387 | * @param {} steps the number of steps to run through before returning 388 | * @return {grid} a new grid after || 1 steps 389 | */ 390 | Terrarium.prototype.step = function (steps) { 391 | function copyAndRemoveInner (origCreature) { 392 | if (origCreature) { 393 | var copy = _.assign(new (origCreature.constructor)(), origCreature); 394 | var dead = copy && copy.isDead(); 395 | if (dead && !self.hasChanged) self.hasChanged = true; 396 | copy.age++; 397 | 398 | return !dead ? copy : false; 399 | } else return false; 400 | } 401 | 402 | function copyAndRemove (origCols) { 403 | return _.map(origCols, copyAndRemoveInner); 404 | } 405 | 406 | // TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner 407 | function zipCoordsWithNeighbors (coords) { 408 | return { 409 | coords: coords, 410 | creature: oldGrid[coords.x][coords.y] 411 | }; 412 | } 413 | 414 | function processLoser (loser) { 415 | var loserCreature = loser.creature; 416 | if (loserCreature) { 417 | loserCreature.failureFn(); 418 | loserCreature.boundEnergy(); 419 | } else { 420 | loser.wait(); 421 | loser.boundEnergy(); 422 | } 423 | } 424 | 425 | function processCreaturesInner (creature, x, y) { 426 | if (creature) { 427 | var neighbors = _.map( 428 | self.getNeighborCoords(x, y, creature.actionRadius), 429 | zipCoordsWithNeighbors 430 | ); 431 | var result = creature.process(neighbors, x, y); 432 | if (typeof result === 'object') { 433 | var eigenColumn = eigenGrid[result.x]; 434 | var returnedCreature = result.creature; 435 | var returnedY = result.y; 436 | 437 | if (!eigenColumn[returnedY]) eigenColumn[returnedY] = []; 438 | 439 | eigenColumn[returnedY].push({ 440 | x: x, 441 | y: y, 442 | creature: returnedCreature 443 | }); 444 | if (!self.hasChanged && result.observed) self.hasChanged = true; 445 | } else { 446 | if (result && !self.hasChanged) self.hasChanged = true; 447 | processLoser(creature); 448 | } 449 | } 450 | } 451 | 452 | function processCreatures (column, x) { 453 | _.each(column, function (creature, y) { processCreaturesInner(creature, x, y); }); 454 | } 455 | 456 | function pickWinnerInner (superposition, x, y) { 457 | if (superposition) { 458 | var winner = superposition.splice(_.random(superposition.length - 1), 1)[0]; 459 | var winnerCreature = winner.creature; 460 | 461 | // clear the original creature's square if successFn returns false 462 | if (!winnerCreature.successFn()) { 463 | newGrid[winner.x][winner.y] = false; 464 | } 465 | // TODO: so many calls to this. Can we just run it once at the start of a step? 466 | winnerCreature.boundEnergy(); 467 | 468 | // put the winner in its rightful place 469 | newGrid[x][y] = winnerCreature; 470 | 471 | // ...and call wait() on the losers. We can do this without 472 | // affecting temporal consistency because all callbacks have 473 | // already been created with prior conditions 474 | _.each(superposition, processLoser); 475 | } 476 | } 477 | 478 | function pickWinner (column, x) { 479 | _.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); }); 480 | } 481 | 482 | var self = this; 483 | var gridWidth = this.width; 484 | var gridHeight = this.height; 485 | var oldGrid = this.grid, newGrid, eigenGrid; 486 | 487 | if (typeof steps !== 'number') steps = 1; 488 | 489 | while (steps--) { 490 | this.hasChanged = false; 491 | 492 | oldGrid = newGrid ? _.clone(newGrid) : this.grid; 493 | 494 | // copy the old grid & remove dead creatures 495 | newGrid = _.map(oldGrid, copyAndRemove); 496 | 497 | // create an empty grid to hold creatures competing for the same square 498 | eigenGrid = this.makeGrid(); 499 | 500 | // Add each creature's intended destination to the eigenGrid 501 | _.each(newGrid, processCreatures); 502 | 503 | // Choose a winner from each of the eigenGrid's superpositions 504 | _.each(eigenGrid, pickWinner); 505 | 506 | if (!this.hasChanged) return false; 507 | } 508 | 509 | return newGrid; 510 | }; 511 | 512 | /** 513 | * Updates the canvas to reflect the current grid 514 | */ 515 | Terrarium.prototype.draw = function () { 516 | display(this.canvas, this.grid, this.cellSize, this.trails, this.background); 517 | }; 518 | 519 | /** 520 | * Starts animating the simulation. Can be called with only a function. 521 | * @param {int} steps the simulation will stop after steps if specified 522 | * @param {Function} fn called as a callback once the animation finishes 523 | */ 524 | Terrarium.prototype.animate = function (steps, fn) { 525 | function tick () { 526 | var grid = self.step(); 527 | if (grid) { 528 | self.grid = grid; 529 | self.draw(); 530 | if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick); 531 | } // if grid hasn't changed || reached last step 532 | self.nextFrame = false; 533 | if (fn) fn(); 534 | } 535 | 536 | if (typeof steps === 'function') { 537 | fn = steps; 538 | steps = null; 539 | } 540 | 541 | if (!this.nextFrame) { 542 | var i = 0; 543 | var self = this; 544 | self.nextFrame = requestAnimationFrame(tick); 545 | } 546 | }; 547 | 548 | /** 549 | * Stops a currently running animation 550 | */ 551 | Terrarium.prototype.stop = function () { 552 | cancelAnimationFrame(this.nextFrame); 553 | this.nextFrame = false; 554 | }; 555 | 556 | /** 557 | * Stops any currently running animation and cleans up the DOM 558 | */ 559 | Terrarium.prototype.destroy = function () { 560 | var canvas = this.canvas; 561 | this.stop(); 562 | canvas.parentNode.removeChild(canvas); 563 | }; 564 | 565 | module.exports = Terrarium; 566 | 567 | },{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(require,module,exports){ 568 | // Seed Math.random() with seedrandom 569 | require('../bower_components/seedrandom/seedrandom.js')('terra :)', {global: true}); 570 | 571 | // an extended custom build of lodash, generated with: 572 | // lodash exports=commonjs include=assign,clone,filter,each,map,random,reduce,some 573 | var _ = require('../lodash_custom/lodash.custom.min.js')._; 574 | 575 | /** 576 | * Takes a cell and returns the coordinates of its neighbors 577 | * @param {int} x0 - x position of cell 578 | * @param {int} y0 - y position of cell 579 | * @param {int} xMax - maximum x index i.e. grid width 580 | * @param {int} yMax - maximum x index i.e. grid height 581 | * @param {int} radius - (default = 1) neighbor radius 582 | * @return {array} - an array of [x, y] pairs of the neighboring cells 583 | */ 584 | _.getNeighborCoordsFn = function (xMax, yMax, vonNeumann, periodic) { 585 | if (periodic) { 586 | if (vonNeumann) { 587 | // periodic von neumann 588 | return function (x0, y0, radius) { 589 | var coords = [], x, rX, y, rY, rYMax; 590 | 591 | for (rX = -radius; rX <= radius; ++rX) { 592 | rYMax = radius - Math.abs(rX); 593 | for (rY = -rYMax; rY <= rYMax; ++rY) { 594 | x = ((rX + x0) % xMax + xMax) % xMax; 595 | y = ((rY + y0) % yMax + yMax) % yMax; 596 | if (x !== x0 || y !== y0) { 597 | coords.push({ 598 | x: x, 599 | y: y 600 | }); 601 | } 602 | } 603 | } 604 | 605 | return coords; 606 | }; 607 | } 608 | else { 609 | // periodic moore 610 | return function (x0, y0, radius) { 611 | var coords = [], x, xLo, xHi, y, yLo, yHi; 612 | 613 | xLo = x0 - radius; 614 | yLo = y0 - radius; 615 | xHi = x0 + radius; 616 | yHi = y0 + radius; 617 | 618 | for (x = xLo; x <= xHi; ++x) { 619 | for (y = yLo; y <= yHi; ++y) { 620 | if (x !== x0 || y !== y0) { 621 | coords.push({ 622 | x: (x % xMax + xMax) % xMax, 623 | y: (y % yMax + yMax) % yMax 624 | }); 625 | } 626 | } 627 | } 628 | 629 | return coords; 630 | }; 631 | } 632 | } else { 633 | // non-periodic, need to restrict to within [0, max) 634 | xMax -= 1; 635 | yMax -= 1; 636 | 637 | if (vonNeumann) { 638 | //non-periodic von-neumann 639 | return function (x0, y0, radius) { 640 | var coords = [], x, rX, y, rY, rYMax; 641 | 642 | for (rX = -radius; rX <= radius; ++rX) { 643 | rYMax = radius - Math.abs(rX); 644 | for (rY = -rYMax; rY <= rYMax; ++rY) { 645 | x = rX + x0; 646 | y = rY + y0; 647 | if (x >= 0 && y >=0 && x <= xMax && y <= yMax && (x !== x0 || y !== y0)) { 648 | coords.push({ 649 | x: x, 650 | y: y 651 | }); 652 | } 653 | } 654 | } 655 | 656 | return coords; 657 | }; 658 | } 659 | else { 660 | // non-periodic moore 661 | return function (x0, y0, radius) { 662 | var coords = [], x, xLo, xHi, y, yLo, yHi; 663 | 664 | xLo = Math.max(0, x0 - radius); 665 | yLo = Math.max(0, y0 - radius); 666 | xHi = Math.min(x0 + radius, xMax); 667 | yHi = Math.min(y0 + radius, yMax); 668 | 669 | for (x = xLo; x <= xHi; ++x) 670 | for (y = yLo; y <= yHi; ++y) 671 | if (x !== x0 || y !== y0) 672 | coords.push({ x: x, y: y }); 673 | 674 | return coords; 675 | }; 676 | } 677 | } 678 | }; 679 | 680 | _.pickRandomWeighted = function (weightedArrays) { 681 | var sum = 0, rand = _.random(100, true); 682 | var cur, i; 683 | for (i = 0, _len = weightedArrays.length; i < _len; i++) { 684 | cur = weightedArrays[i]; 685 | sum += cur[1]; 686 | if (sum > rand) return cur[0]; 687 | } return false; 688 | }; 689 | 690 | /** 691 | * CommonJS exports 692 | * @type {Object} 693 | */ 694 | module.exports = _; 695 | 696 | },{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":8}],7:[function(require,module,exports){ 697 | /** 698 | 699 | seedrandom.js 700 | ============= 701 | 702 | Seeded random number generator for Javascript. 703 | 704 | version 2.3.6
705 | Author: David Bau
706 | Date: 2014 May 14 707 | 708 | Can be used as a plain script, a node.js module or an AMD module. 709 | 710 | Script tag usage 711 | ---------------- 712 | 713 | 715 | 716 | // Sets Math.random to a PRNG initialized using the given explicit seed. 717 | Math.seedrandom('hello.'); 718 | console.log(Math.random()); // Always 0.9282578795792454 719 | console.log(Math.random()); // Always 0.3752569768646784 720 | 721 | // Sets Math.random to an ARC4-based PRNG that is autoseeded using the 722 | // current time, dom state, and other accumulated local entropy. 723 | // The generated seed string is returned. 724 | Math.seedrandom(); 725 | console.log(Math.random()); // Reasonably unpredictable. 726 | 727 | // Seeds using the given explicit seed mixed with accumulated entropy. 728 | Math.seedrandom('added entropy.', { entropy: true }); 729 | console.log(Math.random()); // As unpredictable as added entropy. 730 | 731 | // Use "new" to create a local prng without altering Math.random. 732 | var myrng = new Math.seedrandom('hello.'); 733 | console.log(myrng()); // Always 0.9282578795792454 734 | 735 | 736 | Node.js usage 737 | ------------- 738 | 739 | npm install seedrandom 740 | 741 | // Local PRNG: does not affect Math.random. 742 | var seedrandom = require('seedrandom'); 743 | var rng = seedrandom('hello.'); 744 | console.log(rng()); // Always 0.9282578795792454 745 | 746 | // Autoseeded ARC4-based PRNG. 747 | rng = seedrandom(); 748 | console.log(rng()); // Reasonably unpredictable. 749 | 750 | // Global PRNG: set Math.random. 751 | seedrandom('hello.', { global: true }); 752 | console.log(Math.random()); // Always 0.9282578795792454 753 | 754 | // Mixing accumulated entropy. 755 | rng = seedrandom('added entropy.', { entropy: true }); 756 | console.log(rng()); // As unpredictable as added entropy. 757 | 758 | 759 | Require.js usage 760 | ---------------- 761 | 762 | Similar to node.js usage: 763 | 764 | bower install seedrandom 765 | 766 | require(['seedrandom'], function(seedrandom) { 767 | var rng = seedrandom('hello.'); 768 | console.log(rng()); // Always 0.9282578795792454 769 | }); 770 | 771 | 772 | Network seeding via a script tag 773 | -------------------------------- 774 | 775 | 777 | 778 | 780 | 781 | Examples of manipulating the seed for various purposes: 782 | 783 | var seed = Math.seedrandom(); // Use prng with an automatic seed. 784 | document.write(Math.random()); // Pretty much unpredictable x. 785 | 786 | var rng = new Math.seedrandom(seed); // A new prng with the same seed. 787 | document.write(rng()); // Repeat the 'unpredictable' x. 788 | 789 | function reseed(event, count) { // Define a custom entropy collector. 790 | var t = []; 791 | function w(e) { 792 | t.push([e.pageX, e.pageY, +new Date]); 793 | if (t.length < count) { return; } 794 | document.removeEventListener(event, w); 795 | Math.seedrandom(t, { entropy: true }); 796 | } 797 | document.addEventListener(event, w); 798 | } 799 | reseed('mousemove', 100); // Reseed after 100 mouse moves. 800 | 801 | The "pass" option can be used to get both the prng and the seed. 802 | The following returns both an autoseeded prng and the seed as an object, 803 | without mutating Math.random: 804 | 805 | var obj = Math.seedrandom(null, { pass: function(prng, seed) { 806 | return { random: prng, seed: seed }; 807 | }}); 808 | 809 | 810 | Version notes 811 | ------------- 812 | 813 | The random number sequence is the same as version 1.0 for string seeds. 814 | * Version 2.0 changed the sequence for non-string seeds. 815 | * Version 2.1 speeds seeding and uses window.crypto to autoseed if present. 816 | * Version 2.2 alters non-crypto autoseeding to sweep up entropy from plugins. 817 | * Version 2.3 adds support for "new", module loading, and a null seed arg. 818 | * Version 2.3.1 adds a build environment, module packaging, and tests. 819 | * Version 2.3.4 fixes bugs on IE8, and switches to MIT license. 820 | * Version 2.3.6 adds a readable options object argument. 821 | 822 | The standard ARC4 key scheduler cycles short keys, which means that 823 | seedrandom('ab') is equivalent to seedrandom('abab') and 'ababab'. 824 | Therefore it is a good idea to add a terminator to avoid trivial 825 | equivalences on short string seeds, e.g., Math.seedrandom(str + '\0'). 826 | Starting with version 2.0, a terminator is added automatically for 827 | non-string seeds, so seeding with the number 111 is the same as seeding 828 | with '111\0'. 829 | 830 | When seedrandom() is called with zero args or a null seed, it uses a 831 | seed drawn from the browser crypto object if present. If there is no 832 | crypto support, seedrandom() uses the current time, the native rng, 833 | and a walk of several DOM objects to collect a few bits of entropy. 834 | 835 | Each time the one- or two-argument forms of seedrandom are called, 836 | entropy from the passed seed is accumulated in a pool to help generate 837 | future seeds for the zero- and two-argument forms of seedrandom. 838 | 839 | On speed - This javascript implementation of Math.random() is several 840 | times slower than the built-in Math.random() because it is not native 841 | code, but that is typically fast enough. Some details (timings on 842 | Chrome 25 on a 2010 vintage macbook): 843 | 844 | * seeded Math.random() - avg less than 0.0002 milliseconds per call 845 | * seedrandom('explicit.') - avg less than 0.2 milliseconds per call 846 | * seedrandom('explicit.', true) - avg less than 0.2 milliseconds per call 847 | * seedrandom() with crypto - avg less than 0.2 milliseconds per call 848 | 849 | Autoseeding without crypto is somewhat slower, about 20-30 milliseconds on 850 | a 2012 windows 7 1.5ghz i5 laptop, as seen on Firefox 19, IE 10, and Opera. 851 | Seeded rng calls themselves are fast across these browsers, with slowest 852 | numbers on Opera at about 0.0005 ms per seeded Math.random(). 853 | 854 | 855 | LICENSE (MIT) 856 | ------------- 857 | 858 | Copyright (c)2014 David Bau. 859 | 860 | Permission is hereby granted, free of charge, to any person obtaining 861 | a copy of this software and associated documentation files (the 862 | "Software"), to deal in the Software without restriction, including 863 | without limitation the rights to use, copy, modify, merge, publish, 864 | distribute, sublicense, and/or sell copies of the Software, and to 865 | permit persons to whom the Software is furnished to do so, subject to 866 | the following conditions: 867 | 868 | The above copyright notice and this permission notice shall be 869 | included in all copies or substantial portions of the Software. 870 | 871 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 872 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 873 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 874 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 875 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 876 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 877 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 878 | 879 | */ 880 | 881 | /** 882 | * All code is in an anonymous closure to keep the global namespace clean. 883 | */ 884 | (function ( 885 | global, pool, math, width, chunks, digits, module, define, rngname) { 886 | 887 | // 888 | // The following constants are related to IEEE 754 limits. 889 | // 890 | var startdenom = math.pow(width, chunks), 891 | significance = math.pow(2, digits), 892 | overflow = significance * 2, 893 | mask = width - 1, 894 | 895 | // 896 | // seedrandom() 897 | // This is the seedrandom function described above. 898 | // 899 | impl = math['seed' + rngname] = function(seed, options, callback) { 900 | var key = []; 901 | options = (options == true) ? { entropy: true } : (options || {}); 902 | 903 | // Flatten the seed string or build one from local entropy if needed. 904 | var shortseed = mixkey(flatten( 905 | options.entropy ? [seed, tostring(pool)] : 906 | (seed == null) ? autoseed() : seed, 3), key); 907 | 908 | // Use the seed to initialize an ARC4 generator. 909 | var arc4 = new ARC4(key); 910 | 911 | // Mix the randomness into accumulated entropy. 912 | mixkey(tostring(arc4.S), pool); 913 | 914 | // Calling convention: what to return as a function of prng, seed, is_math. 915 | return (options.pass || callback || 916 | // If called as a method of Math (Math.seedrandom()), mutate Math.random 917 | // because that is how seedrandom.js has worked since v1.0. Otherwise, 918 | // it is a newer calling convention, so return the prng directly. 919 | function(prng, seed, is_math_call) { 920 | if (is_math_call) { math[rngname] = prng; return seed; } 921 | else return prng; 922 | })( 923 | 924 | // This function returns a random double in [0, 1) that contains 925 | // randomness in every bit of the mantissa of the IEEE 754 value. 926 | function() { 927 | var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 928 | d = startdenom, // and denominator d = 2 ^ 48. 929 | x = 0; // and no 'extra last byte'. 930 | while (n < significance) { // Fill up all significant digits by 931 | n = (n + x) * width; // shifting numerator and 932 | d *= width; // denominator and generating a 933 | x = arc4.g(1); // new least-significant-byte. 934 | } 935 | while (n >= overflow) { // To avoid rounding up, before adding 936 | n /= 2; // last byte, shift everything 937 | d /= 2; // right using integer math until 938 | x >>>= 1; // we have exactly the desired bits. 939 | } 940 | return (n + x) / d; // Form the number within [0, 1). 941 | }, shortseed, 'global' in options ? options.global : (this == math)); 942 | }; 943 | 944 | // 945 | // ARC4 946 | // 947 | // An ARC4 implementation. The constructor takes a key in the form of 948 | // an array of at most (width) integers that should be 0 <= x < (width). 949 | // 950 | // The g(count) method returns a pseudorandom integer that concatenates 951 | // the next (count) outputs from ARC4. Its return value is a number x 952 | // that is in the range 0 <= x < (width ^ count). 953 | // 954 | /** @constructor */ 955 | function ARC4(key) { 956 | var t, keylen = key.length, 957 | me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; 958 | 959 | // The empty key [] is treated as [0]. 960 | if (!keylen) { key = [keylen++]; } 961 | 962 | // Set up S using the standard key scheduling algorithm. 963 | while (i < width) { 964 | s[i] = i++; 965 | } 966 | for (i = 0; i < width; i++) { 967 | s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))]; 968 | s[j] = t; 969 | } 970 | 971 | // The "g" method returns the next (count) outputs as one number. 972 | (me.g = function(count) { 973 | // Using instance members instead of closure state nearly doubles speed. 974 | var t, r = 0, 975 | i = me.i, j = me.j, s = me.S; 976 | while (count--) { 977 | t = s[i = mask & (i + 1)]; 978 | r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))]; 979 | } 980 | me.i = i; me.j = j; 981 | return r; 982 | // For robust unpredictability discard an initial batch of values. 983 | // See http://www.rsa.com/rsalabs/node.asp?id=2009 984 | })(width); 985 | } 986 | 987 | // 988 | // flatten() 989 | // Converts an object tree to nested arrays of strings. 990 | // 991 | function flatten(obj, depth) { 992 | var result = [], typ = (typeof obj), prop; 993 | if (depth && typ == 'object') { 994 | for (prop in obj) { 995 | try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} 996 | } 997 | } 998 | return (result.length ? result : typ == 'string' ? obj : obj + '\0'); 999 | } 1000 | 1001 | // 1002 | // mixkey() 1003 | // Mixes a string seed into a key that is an array of integers, and 1004 | // returns a shortened string seed that is equivalent to the result key. 1005 | // 1006 | function mixkey(seed, key) { 1007 | var stringseed = seed + '', smear, j = 0; 1008 | while (j < stringseed.length) { 1009 | key[mask & j] = 1010 | mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); 1011 | } 1012 | return tostring(key); 1013 | } 1014 | 1015 | // 1016 | // autoseed() 1017 | // Returns an object for autoseeding, using window.crypto if available. 1018 | // 1019 | /** @param {Uint8Array|Navigator=} seed */ 1020 | function autoseed(seed) { 1021 | try { 1022 | global.crypto.getRandomValues(seed = new Uint8Array(width)); 1023 | return tostring(seed); 1024 | } catch (e) { 1025 | return [+new Date, global, (seed = global.navigator) && seed.plugins, 1026 | global.screen, tostring(pool)]; 1027 | } 1028 | } 1029 | 1030 | // 1031 | // tostring() 1032 | // Converts an array of charcodes to a string 1033 | // 1034 | function tostring(a) { 1035 | return String.fromCharCode.apply(0, a); 1036 | } 1037 | 1038 | // 1039 | // When seedrandom.js is loaded, we immediately mix a few bits 1040 | // from the built-in RNG into the entropy pool. Because we do 1041 | // not want to intefere with determinstic PRNG state later, 1042 | // seedrandom will not call math.random on its own again after 1043 | // initialization. 1044 | // 1045 | mixkey(math[rngname](), pool); 1046 | 1047 | // 1048 | // Nodejs and AMD support: export the implemenation as a module using 1049 | // either convention. 1050 | // 1051 | if (module && module.exports) { 1052 | module.exports = impl; 1053 | } else if (define && define.amd) { 1054 | define(function() { return impl; }); 1055 | } 1056 | 1057 | // End anonymous scope, and pass initial values. 1058 | })( 1059 | this, // global window object 1060 | [], // pool: entropy pool starts empty 1061 | Math, // math: package containing random, pow, and seedrandom 1062 | 256, // width: each RC4 output is 0 <= x < 256 1063 | 6, // chunks: at least six RC4 outputs for each double 1064 | 52, // digits: there are 52 significant digits in a double 1065 | (typeof module) == 'object' && module, // present in node.js 1066 | (typeof define) == 'function' && define, // present with an AMD loader 1067 | 'random'// rngname: name for Math.random and Math.seedrandom 1068 | ); 1069 | 1070 | },{}],8:[function(require,module,exports){ 1071 | (function (global){ 1072 | /** 1073 | * @license 1074 | * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE 1075 | * Build: `lodash exports="commonjs" include="assign,clone,filter,each,map,random,reduce,some"` 1076 | */ 1077 | ;(function(){function n(n){return typeof n.toString!="function"&&typeof(n+"")=="string"}function t(n){n.length=0,S.lengthe?0:e);++rk;k++)r+="n='"+e.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",e.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+e.g+"}";r+="}"}return(e.b||bt.nonEnumArgs)&&(r+="}"),r+=e.c+";return E",n("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",t+r+"}")(c,$,Y,ut,A,g,mt,v,q.f,Z,H,vt,M,nt,tt) 1086 | }function s(n){return typeof n=="function"&&et.test(n)}function g(n){return n&&typeof n=="object"&&typeof n.length=="number"&&tt.call(n)==B||false}function y(n){return typeof n=="function"}function h(n){return!(!n||!H[typeof n])}function v(n){return typeof n=="string"||n&&typeof n=="object"&&tt.call(n)==M||false}function b(n,t,e){var o=[];if(t=r.createCallback(t,e,3),mt(n)){e=-1;for(var u=n.length;++earguments.length;if(t=r.createCallback(t,o,4),mt(n)){var a=-1,c=n.length;for(u&&(e=n[++a]);++a3&&typeof a[c-2]=='function'){var e=d(a[--c-1],a[c--],2)}else if(c>2&&typeof a[c-1]=='function'){e=a[--c]}"),g:"E[n]=e?e(E[n],t[n]):t[n]"}),Ot=p(U,wt,{j:false}),St=p(U,wt); 1094 | y(/x/)&&(y=function(n){return typeof n=="function"&&"[object Function]"==tt.call(n)}),r.assign=xt,r.bind=w,r.createCallback=function(n,t,e){var r=typeof n;if(null==n||"function"==r)return c(n,t,e);if("object"!=r)return O(n);var o=Et(n),u=o[0],a=n[u];return 1!=o.length||a!==a||h(a)?function(t){for(var e=o.length,r=false;e--&&(r=l(t[o[e]],n[o[e]],null,true)););return r}:function(n){return n=n[u],a===n&&(0!==a||1/a==1/n)}},r.filter=b,r.forEach=d,r.forIn=Ot,r.forOwn=St,r.keys=Et,r.map=m,r.property=O,r.collect=m,r.each=d,r.extend=xt,r.select=b,r.clone=function(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=t,t=false),u(n,t,typeof e=="function"&&c(e,r,1)) 1095 | },r.identity=_,r.isArguments=g,r.isArray=mt,r.isFunction=y,r.isObject=h,r.isString=v,r.noop=x,r.random=function(n,t,e){var r=null==n,o=null==t;return null==e&&(typeof n=="boolean"&&o?(e=n,n=1):o||typeof t!="boolean"||(e=t,o=true)),r&&o&&(t=1),n=+n||0,o?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=yt(),gt(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):n+rt(yt()*(t-n+1))},r.reduce=j,r.some=E,r.any=E,r.foldl=j,r.inject=j,r.VERSION="2.4.1",W&&Q&&(W._=r)}).call(this); 1096 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 1097 | },{}]},{},[1])(1) 1098 | }); -------------------------------------------------------------------------------- /dist/terra.min.js: -------------------------------------------------------------------------------- 1 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.terra=t()}}(function(){var t;return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!u&&c)return c(a,!0);if(i)return i(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var s=n[a]={exports:{}};t[a][0].call(s.exports,function(e){var n=t[a][1][e];return o(n?n:e)},s,s.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;athis.maxEnergy&&(this.energy=this.maxEnergy)},t.prototype.isDead=function(){return this.energy<=0},t.prototype.reproduce=function(t){var e=n.filter(t,function(t){return!t.creature});if(e.length){var o=e[n.random(e.length-1)],i=o.coords,a=r.make(this.type),u=function(){return this.energy-=this.initialEnergy,!0}.bind(this),c=this.wait;return{x:i.x,y:i.y,creature:a,successFn:u,failureFn:c}}return!1},t.prototype.move=function(t){var e=this,r=n.filter(t,function(t){return t.creature.sizen*this.reproduceLv&&this.reproduce?e=this.reproduce(t):this.energy>n*this.moveLv&&this.move&&(e=this.move(t));var r=e.creature;return r?(r.successFn=e.successFn||r.wait,r.failureFn=e.failureFn||r.wait,{x:e.x,y:e.y,creature:r,observed:!0}):this.energy!==this.maxEnergy},e.prototype.actionRadius=1,e.prototype.boundEnergy=function(){},e.prototype.isDead=function(){return!1},e.prototype.process=function(){},e.prototype.wait=function(){};var o={};return{make:function(t,e){var n=o[t];return n?new n(e):!1},registerCreature:function(e,r){var i=e.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){this.energy=this.initialEnergy,r.call(this)}:function(){this.energy=this.initialEnergy};var a=e.color||e.colour;return("object"!=typeof a||3!==a.length)&&(e.color=[n.random(255),n.random(255),n.random(255)]),o[i].prototype=new t,o[i].prototype.constructor=o[i],n.each(e,function(t,e){o[i].prototype[e]=t}),o[i].prototype.successFn=o[i].prototype.wait,o[i].prototype.failureFn=o[i].prototype.wait,o[i].prototype.energy=e.initialEnergy,!0}return!1},registerCA:function(t,r){var i=t.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){r.call(this)}:function(){};var a=t.color=t.color||t.colour;return("object"!=typeof a||3!==a.length)&&(t.color=[n.random(255),n.random(255),n.random(255)]),t.colorFn=t.colorFn||t.colourFn,o[i].prototype=new e,o[i].prototype.constructor=o[i],n.each(t,function(t,e){o[i].prototype[e]=t}),!0}return!1}}}();e.exports=r},{"./util.js":6}],3:[function(t,e){var n=t("./util.js");e.exports=function(t,e,r,o,i){var a=t.getContext("2d");if(o&&i)a.fillStyle="rgba("+i+","+(1-o)+")",a.fillRect(0,0,t.width,t.height);else{if(o)throw"Background must also be set for trails";a.clearRect(0,0,t.width,t.height)}n.each(e,function(t,e){n.each(t,function(t,n){if(t){var o=t.colorFn?t.colorFn():t.color+","+t.energy/t.maxEnergy;a.fillStyle="rgba("+o+")",t.character?a.fillText(t.character,e*r,n*r+r):a.fillRect(e*r,n*r,r,r)}})})}},{"./util.js":6}],4:[function(t,e){var n=function(t,e,n,r,o,i){function a(){var o=document.createElement("canvas"),a=o.getContext("2d"),u=function(){var t=document.createElement("canvas").getContext("2d"),e=window.devicePixelRatio||1,n=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/n}();return o.width=t*u,o.height=e*u,o.style.width=t+"px",o.style.height=e+"px",a.scale(u,u),a.font="bold "+n+"px Arial",r&&(o.id=r),i&&(o.style.background="rgb("+i+")"),o}t*=n,e*=n;var u=a();return o?o.parentNode.insertBefore(u,o.nextSibling):document.body.appendChild(u),u};e.exports={createCanvasElement:n}},{}],5:[function(t,e){function n(t,e,n){var o,i;t=Math.ceil(t),e=Math.ceil(e),n=n||{},o=n.cellSize||10,i=n.neighborhood||n.neighbourhood,"string"==typeof i&&(i=i.toLowerCase()),this.width=t,this.height=e,this.cellSize=o,this.trails=n.trails,this.background=n.background,this.canvas=a.createCanvasElement(t,e,o,n.id,n.insertAfter,this.background),this.grid=[],this.nextFrame=!1,this.hasChanged=!1,this.getNeighborCoords=r.getNeighborCoordsFn(t,e,"vonneumann"===i,n.periodic)}var r=t("./util"),o=t("./creature.js"),i=t("./display.js"),a=t("./dom.js");n.prototype.makeGrid=function(t){for(var e=[],n=typeof t,r=0,i=this.width;i>r;r++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[r].push(o.make("function"===n?t(r,a):"object"===n&&t.length?(t[a]||[])[r]:"string"===n?t:void 0))}return e},n.prototype.makeGridWithDistribution=function(t){for(var e=[],n=0,i=this.width;i>n;n++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[n].push(o.make(r.pickRandomWeighted(t)))}return e},n.prototype.step=function(t){function e(t){if(t){var e=r.assign(new t.constructor,t),n=e&&e.isDead();return n&&!p.hasChanged&&(p.hasChanged=!0),e.age++,n?!1:e}return!1}function n(t){return r.map(t,e)}function o(t){return{coords:t,creature:h[t.x][t.y]}}function i(t){var e=t.creature;e?(e.failureFn(),e.boundEnergy()):(t.wait(),t.boundEnergy())}function a(t,e,n){if(t){var a=r.map(p.getNeighborCoords(e,n,t.actionRadius),o),u=t.process(a,e,n);if("object"==typeof u){var c=l[u.x],f=u.creature,s=u.y;c[s]||(c[s]=[]),c[s].push({x:e,y:n,creature:f}),!p.hasChanged&&u.observed&&(p.hasChanged=!0)}else u&&!p.hasChanged&&(p.hasChanged=!0),i(t)}}function u(t,e){r.each(t,function(t,n){a(t,e,n)})}function c(t,e,n){if(t){var o=t.splice(r.random(t.length-1),1)[0],a=o.creature;a.successFn()||(s[o.x][o.y]=!1),a.boundEnergy(),s[e][n]=a,r.each(t,i)}}function f(t,e){r.each(t,function(t,n){c(t,e,n)})}var s,l,p=this,h=(this.width,this.height,this.grid);for("number"!=typeof t&&(t=1);t--;)if(this.hasChanged=!1,h=s?r.clone(s):this.grid,s=r.map(h,n),l=this.makeGrid(),r.each(s,u),r.each(l,f),!this.hasChanged)return!1;return s},n.prototype.draw=function(){i(this.canvas,this.grid,this.cellSize,this.trails,this.background)},n.prototype.animate=function(t,e){function n(){var i=o.step();return i&&(o.grid=i,o.draw(),++r!==t)?o.nextFrame=requestAnimationFrame(n):(o.nextFrame=!1,void(e&&e()))}if("function"==typeof t&&(e=t,t=null),!this.nextFrame){var r=0,o=this;o.nextFrame=requestAnimationFrame(n)}},n.prototype.stop=function(){cancelAnimationFrame(this.nextFrame),this.nextFrame=!1},n.prototype.destroy=function(){var t=this.canvas;this.stop(),t.parentNode.removeChild(t)},e.exports=n},{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(t,e){t("../bower_components/seedrandom/seedrandom.js")("terra :)",{global:!0});var n=t("../lodash_custom/lodash.custom.min.js")._;n.getNeighborCoordsFn=function(t,e,n,r){return r?n?function(n,r,o){var i,a,u,c,f,s=[];for(a=-o;o>=a;++a)for(f=o-Math.abs(a),c=-f;f>=c;++c)i=((a+n)%t+t)%t,u=((c+r)%e+e)%e,(i!==n||u!==r)&&s.push({x:i,y:u});return s}:function(n,r,o){var i,a,u,c,f,s,l=[];for(a=n-o,f=r-o,u=n+o,s=r+o,i=a;u>=i;++i)for(c=f;s>=c;++c)(i!==n||c!==r)&&l.push({x:(i%t+t)%t,y:(c%e+e)%e});return l}:(t-=1,e-=1,n?function(n,r,o){var i,a,u,c,f,s=[];for(a=-o;o>=a;++a)for(f=o-Math.abs(a),c=-f;f>=c;++c)i=a+n,u=c+r,i>=0&&u>=0&&t>=i&&e>=u&&(i!==n||u!==r)&&s.push({x:i,y:u});return s}:function(n,r,o){var i,a,u,c,f,s,l=[];for(a=Math.max(0,n-o),f=Math.max(0,r-o),u=Math.min(n+o,t),s=Math.min(r+o,e),i=a;u>=i;++i)for(c=f;s>=c;++c)(i!==n||c!==r)&&l.push({x:i,y:c});return l})},n.pickRandomWeighted=function(t){var e,r,o=0,i=n.random(100,!0);for(r=0,_len=t.length;_len>r;r++)if(e=t[r],o+=e[1],o>i)return e[0];return!1},e.exports=n},{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":8}],7:[function(e,n){!function(t,e,n,r,o,i,a,u,c){function f(t){var e,n=t.length,o=this,i=0,a=o.i=o.j=0,u=o.S=[];for(n||(t=[n++]);r>i;)u[i]=i++;for(i=0;r>i;i++)u[i]=u[a=v&a+t[i%n]+(e=u[i])],u[a]=e;(o.g=function(t){for(var e,n=0,i=o.i,a=o.j,u=o.S;t--;)e=u[i=v&i+1],n=n*r+u[v&(u[i]=u[a=v&a+e])+(u[a]=e)];return o.i=i,o.j=a,n})(r)}function s(t,e){var n,r=[],o=typeof t;if(e&&"object"==o)for(n in t)try{r.push(s(t[n],e-1))}catch(i){}return r.length?r:"string"==o?t:t+"\x00"}function l(t,e){for(var n,r=t+"",o=0;ot;)t=(t+n)*r,e*=r,n=m.g(1);for(;t>=d;)t/=2,e/=2,n>>>=1;return(t+n)/e},v,"global"in i?i.global:this==n)};l(n[c](),e),a&&a.exports?a.exports=m:u&&u.amd&&u(function(){return m})}(this,[],Math,256,6,52,"object"==typeof n&&n,"function"==typeof t&&t,"random")},{}],8:[function(t,e,n){(function(t){(function(){function r(t){return"function"!=typeof t.toString&&"string"==typeof(t+"")}function o(t){t.length=0,A.lengthn?0:n);++rk;k++)r+="n='"+n.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",n.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+n.g+"}";r+="}"}return(n.b||xe.nonEnumArgs)&&(r+="}"),r+=n.c+";return E",t("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",e+r+"}")(s,T,ee,ce,R,d,je,b,V.f,ne,H,be,U,re,oe)}function g(t){return"function"==typeof t&&ie.test(t)}function d(t){return t&&"object"==typeof t&&"number"==typeof t.length&&oe.call(t)==z||!1}function v(t){return"function"==typeof t}function m(t){return!(!t||!H[typeof t])}function b(t){return"string"==typeof t||t&&"object"==typeof t&&oe.call(t)==U||!1}function x(t,e,n){var r=[];if(e=a.createCallback(e,n,3),je(t)){n=-1;for(var o=t.length;++narguments.length;if(e=a.createCallback(e,r,4),je(t)){var i=-1,u=t.length;for(o&&(n=t[++i]);++i3&&typeof a[c-2]=='function'){var e=d(a[--c-1],a[c--],2)}else if(c>2&&typeof a[c-1]=='function'){e=a[--c]}"),g:"E[n]=e?e(E[n],t[n]):t[n]"}),Se=y(Z,Fe,{j:!1}),Oe=y(Z,Fe);v(/x/)&&(v=function(t){return"function"==typeof t&&"[object Function]"==oe.call(t)}),a.assign=ke,a.bind=F,a.createCallback=function(t,e,n){var r=typeof t;if(null==t||"function"==r)return s(t,e,n);if("object"!=r)return O(t);var o=Ce(t),i=o[0],a=t[i];return 1!=o.length||a!==a||m(a)?function(e){for(var n=o.length,r=!1;n--&&(r=p(e[o[n]],t[o[n]],null,!0)););return r}:function(t){return t=t[i],a===t&&(0!==a||1/a==1/t)}},a.filter=x,a.forEach=w,a.forIn=Se,a.forOwn=Oe,a.keys=Ce,a.map=j,a.property=O,a.collect=j,a.each=w,a.extend=ke,a.select=x,a.clone=function(t,e,n,r){return"boolean"!=typeof e&&null!=e&&(r=n,n=e,e=!1),c(t,e,"function"==typeof n&&s(n,r,1))},a.identity=_,a.isArguments=d,a.isArray=je,a.isFunction=v,a.isObject=m,a.isString=b,a.noop=S,a.random=function(t,e,n){var r=null==t,o=null==e;return null==n&&("boolean"==typeof t&&o?(n=t,t=1):o||"boolean"!=typeof e||(n=e,o=!0)),r&&o&&(e=1),t=+t||0,o?(e=t,t=0):e=+e||0,n||t%1||e%1?(n=ve(),de(t+n*(e-t+parseFloat("1e-"+((n+"").length-1))),e)):t+ae(ve()*(e-t+1))},a.reduce=E,a.some=C,a.any=C,a.foldl=E,a.inject=E,a.VERSION="2.4.1",X&&Y&&(X._=a)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1])(1)}); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var $ = require('gulp-load-plugins')({lazy: false}); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | 6 | var argv = require('minimist')(process.argv.slice(2)); 7 | 8 | var paths = { 9 | app: { 10 | entry: './app/main.js', 11 | all: './app/**/*.js', 12 | ext: ['./bower_components/**/*.js', './lodash_custom/**/*.js'] 13 | }, 14 | demo: { 15 | entry: './demo/scripts/main.js', 16 | scripts: './demo/scripts/**/*.js', 17 | extraScripts: [ 18 | './bower_components/smooth-scroll.js/dist/js/bind-polyfill.min.js', 19 | './bower_components/smooth-scroll.js/dist/js/smooth-scroll.min.js', 20 | './demo/scripts/prism.js', 21 | './demo/scripts/analytics.js' 22 | ], 23 | stylesheets: { 24 | css: './demo/stylesheets/**/*.css', 25 | sass: './demo/stylesheets/**/*.sass' 26 | }, 27 | temp: './demo/temp' 28 | }, 29 | dist: { 30 | scripts: './dist', 31 | demo: './demo' 32 | }, 33 | tests: './tests' 34 | }; 35 | 36 | gulp.task('lint', function() { 37 | return gulp.src(paths.app.all) 38 | .pipe($.jshint()) 39 | .pipe($.jshint.reporter('default')); 40 | }); 41 | 42 | gulp.task('test', function() { 43 | return gulp.src(paths.tests) 44 | .pipe( $.mocha( { reporter: 'spec' } ) ) 45 | }); 46 | 47 | gulp.task('scripts', ['lint'], function() { 48 | return browserify(paths.app.entry, { 49 | debug: argv.debug, 50 | standalone: 'terra' 51 | }) 52 | .bundle() 53 | .pipe(source('terra.js')) 54 | .pipe(gulp.dest(paths.dist.scripts)) 55 | .pipe($.rename('terra.min.js')) 56 | .pipe($.streamify( $.uglify() )) 57 | .pipe(gulp.dest(paths.dist.scripts)) 58 | }); 59 | 60 | gulp.task('demo', function() { 61 | return browserify(paths.demo.entry, { 62 | debug: argv.debug 63 | }) 64 | .bundle() 65 | .pipe(source('temp.js')) 66 | .pipe(gulp.dest(paths.demo.temp)) 67 | }); 68 | 69 | gulp.task('sass', function () { 70 | return gulp.src(paths.demo.stylesheets.sass) 71 | .pipe($.rubySass()) 72 | .pipe($.autoprefixer()) 73 | .pipe(gulp.dest(paths.demo.temp)) 74 | }); 75 | 76 | gulp.task('js_concat', ['demo'], function () { 77 | return gulp.src(paths.demo.extraScripts.concat(paths.demo.temp + '/*.js')) 78 | .pipe($.concat('terra.demo.min.js')) 79 | .pipe($.streamify( $.uglify() )) 80 | .pipe(gulp.dest(paths.dist.demo)) 81 | }); 82 | 83 | gulp.task('css_concat', ['sass'], function () { 84 | return gulp.src([paths.demo.stylesheets.css, paths.demo.temp + '/*.css']) 85 | .pipe($.concat('main.css')) 86 | .pipe($.minifyCss()) 87 | .pipe(gulp.dest(paths.dist.demo)) 88 | }); 89 | 90 | gulp.task('watch', function() { 91 | gulp.watch([paths.app.all, paths.app.ext], ['lint', 'scripts']); 92 | }); 93 | 94 | gulp.task('watchSite', function() { 95 | gulp.watch([paths.app.all, paths.app.ext], ['lint', 'scripts']); 96 | gulp.watch(paths.demo.scripts, ['demo','js_concat']); 97 | gulp.watch([paths.demo.stylesheets.sass, paths.demo.stylesheets.css], ['sass', 'css_concat']); 98 | }); 99 | 100 | gulp.task('deploy', function () { 101 | gulp.src(paths.dist.demo + '/*.*') 102 | .pipe($.ghPages('https://github.com/rileyjshaw/terra.git', 'origin')); 103 | }); 104 | 105 | gulp.task('webserver', function() { 106 | gulp.src(paths.dist.demo) 107 | .pipe($.webserver({ 108 | host: '0.0.0.0', 109 | livereload: true, 110 | open: true 111 | })); 112 | }); 113 | 114 | gulp.task( 'default', [ 'lint', 'scripts', 'watch' ] ); 115 | gulp.task( 'site', [ 'lint', 'scripts', 'demo', 'js_concat', 'sass', 'css_concat', 'webserver', 'watchSite' ] ); 116 | -------------------------------------------------------------------------------- /lodash_custom/lodash.custom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Lo-Dash 2.4.1 (Custom Build) 4 | * Build: `lodash exports="commonjs" include="assign,clone,filter,each,map,random,reduce,some"` 5 | * Copyright 2012-2013 The Dojo Foundation 6 | * Based on Underscore.js 1.5.2 7 | * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 8 | * Available under MIT license 9 | */ 10 | ;(function() { 11 | 12 | /** Used to pool arrays and objects used internally */ 13 | var arrayPool = []; 14 | 15 | /** Used internally to indicate various things */ 16 | var indicatorObject = {}; 17 | 18 | /** Used as the max size of the `arrayPool` and `objectPool` */ 19 | var maxPoolSize = 40; 20 | 21 | /** Used to match regexp flags from their coerced string values */ 22 | var reFlags = /\w*$/; 23 | 24 | /** Used to detected named functions */ 25 | var reFuncName = /^\s*function[ \n\r\t]+\w/; 26 | 27 | /** Used to detect functions containing a `this` reference */ 28 | var reThis = /\bthis\b/; 29 | 30 | /** Used to fix the JScript [[DontEnum]] bug */ 31 | var shadowedProps = [ 32 | 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 33 | 'toLocaleString', 'toString', 'valueOf' 34 | ]; 35 | 36 | /** `Object#toString` result shortcuts */ 37 | var argsClass = '[object Arguments]', 38 | arrayClass = '[object Array]', 39 | boolClass = '[object Boolean]', 40 | dateClass = '[object Date]', 41 | errorClass = '[object Error]', 42 | funcClass = '[object Function]', 43 | numberClass = '[object Number]', 44 | objectClass = '[object Object]', 45 | regexpClass = '[object RegExp]', 46 | stringClass = '[object String]'; 47 | 48 | /** Used to identify object classifications that `_.clone` supports */ 49 | var cloneableClasses = {}; 50 | cloneableClasses[funcClass] = false; 51 | cloneableClasses[argsClass] = cloneableClasses[arrayClass] = 52 | cloneableClasses[boolClass] = cloneableClasses[dateClass] = 53 | cloneableClasses[numberClass] = cloneableClasses[objectClass] = 54 | cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; 55 | 56 | /** Used as the property descriptor for `__bindData__` */ 57 | var descriptor = { 58 | 'configurable': false, 59 | 'enumerable': false, 60 | 'value': null, 61 | 'writable': false 62 | }; 63 | 64 | /** Used as the data object for `iteratorTemplate` */ 65 | var iteratorData = { 66 | 'args': '', 67 | 'array': null, 68 | 'bottom': '', 69 | 'firstArg': '', 70 | 'init': '', 71 | 'keys': null, 72 | 'loop': '', 73 | 'shadowedProps': null, 74 | 'support': null, 75 | 'top': '', 76 | 'useHas': false 77 | }; 78 | 79 | /** Used to determine if values are of the language type Object */ 80 | var objectTypes = { 81 | 'boolean': false, 82 | 'function': true, 83 | 'object': true, 84 | 'number': false, 85 | 'string': false, 86 | 'undefined': false 87 | }; 88 | 89 | /** Used as a reference to the global object */ 90 | var root = (objectTypes[typeof window] && window) || this; 91 | 92 | /** Detect free variable `exports` */ 93 | var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; 94 | 95 | /** Detect free variable `module` */ 96 | var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; 97 | 98 | /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ 99 | var freeGlobal = objectTypes[typeof global] && global; 100 | if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { 101 | root = freeGlobal; 102 | } 103 | 104 | /*--------------------------------------------------------------------------*/ 105 | 106 | /** 107 | * Gets an array from the array pool or creates a new one if the pool is empty. 108 | * 109 | * @private 110 | * @returns {Array} The array from the pool. 111 | */ 112 | function getArray() { 113 | return arrayPool.pop() || []; 114 | } 115 | 116 | /** 117 | * Checks if `value` is a DOM node in IE < 9. 118 | * 119 | * @private 120 | * @param {*} value The value to check. 121 | * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. 122 | */ 123 | function isNode(value) { 124 | // IE < 9 presents DOM nodes as `Object` objects except they have `toString` 125 | // methods that are `typeof` "string" and still can coerce nodes to strings 126 | return typeof value.toString != 'function' && typeof (value + '') == 'string'; 127 | } 128 | 129 | /** 130 | * Releases the given array back to the array pool. 131 | * 132 | * @private 133 | * @param {Array} [array] The array to release. 134 | */ 135 | function releaseArray(array) { 136 | array.length = 0; 137 | if (arrayPool.length < maxPoolSize) { 138 | arrayPool.push(array); 139 | } 140 | } 141 | 142 | /** 143 | * Slices the `collection` from the `start` index up to, but not including, 144 | * the `end` index. 145 | * 146 | * Note: This function is used instead of `Array#slice` to support node lists 147 | * in IE < 9 and to ensure dense arrays are returned. 148 | * 149 | * @private 150 | * @param {Array|Object|string} collection The collection to slice. 151 | * @param {number} start The start index. 152 | * @param {number} end The end index. 153 | * @returns {Array} Returns the new array. 154 | */ 155 | function slice(array, start, end) { 156 | start || (start = 0); 157 | if (typeof end == 'undefined') { 158 | end = array ? array.length : 0; 159 | } 160 | var index = -1, 161 | length = end - start || 0, 162 | result = Array(length < 0 ? 0 : length); 163 | 164 | while (++index < length) { 165 | result[index] = array[start + index]; 166 | } 167 | return result; 168 | } 169 | 170 | /*--------------------------------------------------------------------------*/ 171 | 172 | /** 173 | * Used for `Array` method references. 174 | * 175 | * Normally `Array.prototype` would suffice, however, using an array literal 176 | * avoids issues in Narwhal. 177 | */ 178 | var arrayRef = []; 179 | 180 | /** Used for native method references */ 181 | var errorProto = Error.prototype, 182 | objectProto = Object.prototype, 183 | stringProto = String.prototype; 184 | 185 | /** Used to resolve the internal [[Class]] of values */ 186 | var toString = objectProto.toString; 187 | 188 | /** Used to detect if a method is native */ 189 | var reNative = RegExp('^' + 190 | String(toString) 191 | .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') 192 | .replace(/toString| for [^\]]+/g, '.*?') + '$' 193 | ); 194 | 195 | /** Native method shortcuts */ 196 | var floor = Math.floor, 197 | fnToString = Function.prototype.toString, 198 | hasOwnProperty = objectProto.hasOwnProperty, 199 | push = arrayRef.push, 200 | propertyIsEnumerable = objectProto.propertyIsEnumerable, 201 | unshift = arrayRef.unshift; 202 | 203 | /** Used to set meta data on functions */ 204 | var defineProperty = (function() { 205 | // IE 8 only accepts DOM elements 206 | try { 207 | var o = {}, 208 | func = isNative(func = Object.defineProperty) && func, 209 | result = func(o, o, o) && func; 210 | } catch(e) { } 211 | return result; 212 | }()); 213 | 214 | /* Native method shortcuts for methods with the same name as other `lodash` methods */ 215 | var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, 216 | nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, 217 | nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys, 218 | nativeMin = Math.min, 219 | nativeRandom = Math.random; 220 | 221 | /** Used to lookup a built-in constructor by [[Class]] */ 222 | var ctorByClass = {}; 223 | ctorByClass[arrayClass] = Array; 224 | ctorByClass[boolClass] = Boolean; 225 | ctorByClass[dateClass] = Date; 226 | ctorByClass[funcClass] = Function; 227 | ctorByClass[objectClass] = Object; 228 | ctorByClass[numberClass] = Number; 229 | ctorByClass[regexpClass] = RegExp; 230 | ctorByClass[stringClass] = String; 231 | 232 | /** Used to avoid iterating non-enumerable properties in IE < 9 */ 233 | var nonEnumProps = {}; 234 | nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; 235 | nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; 236 | nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; 237 | nonEnumProps[objectClass] = { 'constructor': true }; 238 | 239 | (function() { 240 | var length = shadowedProps.length; 241 | while (length--) { 242 | var key = shadowedProps[length]; 243 | for (var className in nonEnumProps) { 244 | if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) { 245 | nonEnumProps[className][key] = false; 246 | } 247 | } 248 | } 249 | }()); 250 | 251 | /*--------------------------------------------------------------------------*/ 252 | 253 | /** 254 | * Creates a `lodash` object which wraps the given value to enable intuitive 255 | * method chaining. 256 | * 257 | * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: 258 | * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, 259 | * and `unshift` 260 | * 261 | * Chaining is supported in custom builds as long as the `value` method is 262 | * implicitly or explicitly included in the build. 263 | * 264 | * The chainable wrapper functions are: 265 | * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, 266 | * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, 267 | * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, 268 | * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, 269 | * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, 270 | * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, 271 | * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, 272 | * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, 273 | * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, 274 | * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, 275 | * and `zip` 276 | * 277 | * The non-chainable wrapper functions are: 278 | * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, 279 | * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, 280 | * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, 281 | * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, 282 | * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, 283 | * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, 284 | * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, 285 | * `template`, `unescape`, `uniqueId`, and `value` 286 | * 287 | * The wrapper functions `first` and `last` return wrapped values when `n` is 288 | * provided, otherwise they return unwrapped values. 289 | * 290 | * Explicit chaining can be enabled by using the `_.chain` method. 291 | * 292 | * @name _ 293 | * @constructor 294 | * @category Chaining 295 | * @param {*} value The value to wrap in a `lodash` instance. 296 | * @returns {Object} Returns a `lodash` instance. 297 | * @example 298 | * 299 | * var wrapped = _([1, 2, 3]); 300 | * 301 | * // returns an unwrapped value 302 | * wrapped.reduce(function(sum, num) { 303 | * return sum + num; 304 | * }); 305 | * // => 6 306 | * 307 | * // returns a wrapped value 308 | * var squares = wrapped.map(function(num) { 309 | * return num * num; 310 | * }); 311 | * 312 | * _.isArray(squares); 313 | * // => false 314 | * 315 | * _.isArray(squares.value()); 316 | * // => true 317 | */ 318 | function lodash() { 319 | // no operation performed 320 | } 321 | 322 | /** 323 | * An object used to flag environments features. 324 | * 325 | * @static 326 | * @memberOf _ 327 | * @type Object 328 | */ 329 | var support = lodash.support = {}; 330 | 331 | (function() { 332 | var ctor = function() { this.x = 1; }, 333 | object = { '0': 1, 'length': 1 }, 334 | props = []; 335 | 336 | ctor.prototype = { 'valueOf': 1, 'y': 1 }; 337 | for (var key in new ctor) { props.push(key); } 338 | for (key in arguments) { } 339 | 340 | /** 341 | * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). 342 | * 343 | * @memberOf _.support 344 | * @type boolean 345 | */ 346 | support.argsClass = toString.call(arguments) == argsClass; 347 | 348 | /** 349 | * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). 350 | * 351 | * @memberOf _.support 352 | * @type boolean 353 | */ 354 | support.argsObject = arguments.constructor == Object && !(arguments instanceof Array); 355 | 356 | /** 357 | * Detect if `name` or `message` properties of `Error.prototype` are 358 | * enumerable by default. (IE < 9, Safari < 5.1) 359 | * 360 | * @memberOf _.support 361 | * @type boolean 362 | */ 363 | support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); 364 | 365 | /** 366 | * Detect if `prototype` properties are enumerable by default. 367 | * 368 | * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 369 | * (if the prototype or a property on the prototype has been set) 370 | * incorrectly sets a function's `prototype` property [[Enumerable]] 371 | * value to `true`. 372 | * 373 | * @memberOf _.support 374 | * @type boolean 375 | */ 376 | support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); 377 | 378 | /** 379 | * Detect if functions can be decompiled by `Function#toString` 380 | * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). 381 | * 382 | * @memberOf _.support 383 | * @type boolean 384 | */ 385 | support.funcDecomp = !isNative(root.WinRTError) && reThis.test(function() { return this; }); 386 | 387 | /** 388 | * Detect if `Function#name` is supported (all but IE). 389 | * 390 | * @memberOf _.support 391 | * @type boolean 392 | */ 393 | support.funcNames = typeof Function.name == 'string'; 394 | 395 | /** 396 | * Detect if `arguments` object indexes are non-enumerable 397 | * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). 398 | * 399 | * @memberOf _.support 400 | * @type boolean 401 | */ 402 | support.nonEnumArgs = key != 0; 403 | 404 | /** 405 | * Detect if properties shadowing those on `Object.prototype` are non-enumerable. 406 | * 407 | * In IE < 9 an objects own properties, shadowing non-enumerable ones, are 408 | * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). 409 | * 410 | * @memberOf _.support 411 | * @type boolean 412 | */ 413 | support.nonEnumShadows = !/valueOf/.test(props); 414 | 415 | /** 416 | * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. 417 | * 418 | * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` 419 | * and `splice()` functions that fail to remove the last element, `value[0]`, 420 | * of array-like objects even though the `length` property is set to `0`. 421 | * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` 422 | * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. 423 | * 424 | * @memberOf _.support 425 | * @type boolean 426 | */ 427 | support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); 428 | 429 | /** 430 | * Detect lack of support for accessing string characters by index. 431 | * 432 | * IE < 8 can't access characters by index and IE 8 can only access 433 | * characters by index on string literals. 434 | * 435 | * @memberOf _.support 436 | * @type boolean 437 | */ 438 | support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; 439 | 440 | /** 441 | * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) 442 | * and that the JS engine errors when attempting to coerce an object to 443 | * a string without a `toString` function. 444 | * 445 | * @memberOf _.support 446 | * @type boolean 447 | */ 448 | try { 449 | support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + '')); 450 | } catch(e) { 451 | support.nodeClass = true; 452 | } 453 | }(1)); 454 | 455 | /*--------------------------------------------------------------------------*/ 456 | 457 | /** 458 | * The template used to create iterator functions. 459 | * 460 | * @private 461 | * @param {Object} data The data object used to populate the text. 462 | * @returns {string} Returns the interpolated text. 463 | */ 464 | var iteratorTemplate = function(obj) { 465 | 466 | var __p = 'var index, iterable = ' + 467 | (obj.firstArg) + 468 | ', result = ' + 469 | (obj.init) + 470 | ';\nif (!iterable) return result;\n' + 471 | (obj.top) + 472 | ';'; 473 | if (obj.array) { 474 | __p += '\nvar length = iterable.length; index = -1;\nif (' + 475 | (obj.array) + 476 | ') { '; 477 | if (support.unindexedChars) { 478 | __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } '; 479 | } 480 | __p += '\n while (++index < length) {\n ' + 481 | (obj.loop) + 482 | ';\n }\n}\nelse { '; 483 | } else if (support.nonEnumArgs) { 484 | __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' + 485 | (obj.loop) + 486 | ';\n }\n } else { '; 487 | } 488 | 489 | if (support.enumPrototypes) { 490 | __p += '\n var skipProto = typeof iterable == \'function\';\n '; 491 | } 492 | 493 | if (support.enumErrorProps) { 494 | __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n '; 495 | } 496 | 497 | var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); } 498 | 499 | if (obj.useHas && obj.keys) { 500 | __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n'; 501 | if (conditions.length) { 502 | __p += ' if (' + 503 | (conditions.join(' && ')) + 504 | ') {\n '; 505 | } 506 | __p += 507 | (obj.loop) + 508 | '; '; 509 | if (conditions.length) { 510 | __p += '\n }'; 511 | } 512 | __p += '\n } '; 513 | } else { 514 | __p += '\n for (index in iterable) {\n'; 515 | if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) { 516 | __p += ' if (' + 517 | (conditions.join(' && ')) + 518 | ') {\n '; 519 | } 520 | __p += 521 | (obj.loop) + 522 | '; '; 523 | if (conditions.length) { 524 | __p += '\n }'; 525 | } 526 | __p += '\n } '; 527 | if (support.nonEnumShadows) { 528 | __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n '; 529 | for (k = 0; k < 7; k++) { 530 | __p += '\n index = \'' + 531 | (obj.shadowedProps[k]) + 532 | '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))'; 533 | if (!obj.useHas) { 534 | __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])'; 535 | } 536 | __p += ') {\n ' + 537 | (obj.loop) + 538 | ';\n } '; 539 | } 540 | __p += '\n } '; 541 | } 542 | 543 | } 544 | 545 | if (obj.array || support.nonEnumArgs) { 546 | __p += '\n}'; 547 | } 548 | __p += 549 | (obj.bottom) + 550 | ';\nreturn result'; 551 | 552 | return __p 553 | }; 554 | 555 | /*--------------------------------------------------------------------------*/ 556 | 557 | /** 558 | * The base implementation of `_.bind` that creates the bound function and 559 | * sets its meta data. 560 | * 561 | * @private 562 | * @param {Array} bindData The bind data array. 563 | * @returns {Function} Returns the new bound function. 564 | */ 565 | function baseBind(bindData) { 566 | var func = bindData[0], 567 | partialArgs = bindData[2], 568 | thisArg = bindData[4]; 569 | 570 | function bound() { 571 | // `Function#bind` spec 572 | // http://es5.github.io/#x15.3.4.5 573 | if (partialArgs) { 574 | // avoid `arguments` object deoptimizations by using `slice` instead 575 | // of `Array.prototype.slice.call` and not assigning `arguments` to a 576 | // variable as a ternary expression 577 | var args = slice(partialArgs); 578 | push.apply(args, arguments); 579 | } 580 | // mimic the constructor's `return` behavior 581 | // http://es5.github.io/#x13.2.2 582 | if (this instanceof bound) { 583 | // ensure `new bound` is an instance of `func` 584 | var thisBinding = baseCreate(func.prototype), 585 | result = func.apply(thisBinding, args || arguments); 586 | return isObject(result) ? result : thisBinding; 587 | } 588 | return func.apply(thisArg, args || arguments); 589 | } 590 | setBindData(bound, bindData); 591 | return bound; 592 | } 593 | 594 | /** 595 | * The base implementation of `_.clone` without argument juggling or support 596 | * for `thisArg` binding. 597 | * 598 | * @private 599 | * @param {*} value The value to clone. 600 | * @param {boolean} [isDeep=false] Specify a deep clone. 601 | * @param {Function} [callback] The function to customize cloning values. 602 | * @param {Array} [stackA=[]] Tracks traversed source objects. 603 | * @param {Array} [stackB=[]] Associates clones with source counterparts. 604 | * @returns {*} Returns the cloned value. 605 | */ 606 | function baseClone(value, isDeep, callback, stackA, stackB) { 607 | if (callback) { 608 | var result = callback(value); 609 | if (typeof result != 'undefined') { 610 | return result; 611 | } 612 | } 613 | // inspect [[Class]] 614 | var isObj = isObject(value); 615 | if (isObj) { 616 | var className = toString.call(value); 617 | if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) { 618 | return value; 619 | } 620 | var ctor = ctorByClass[className]; 621 | switch (className) { 622 | case boolClass: 623 | case dateClass: 624 | return new ctor(+value); 625 | 626 | case numberClass: 627 | case stringClass: 628 | return new ctor(value); 629 | 630 | case regexpClass: 631 | result = ctor(value.source, reFlags.exec(value)); 632 | result.lastIndex = value.lastIndex; 633 | return result; 634 | } 635 | } else { 636 | return value; 637 | } 638 | var isArr = isArray(value); 639 | if (isDeep) { 640 | // check for circular references and return corresponding clone 641 | var initedStack = !stackA; 642 | stackA || (stackA = getArray()); 643 | stackB || (stackB = getArray()); 644 | 645 | var length = stackA.length; 646 | while (length--) { 647 | if (stackA[length] == value) { 648 | return stackB[length]; 649 | } 650 | } 651 | result = isArr ? ctor(value.length) : {}; 652 | } 653 | else { 654 | result = isArr ? slice(value) : assign({}, value); 655 | } 656 | // add array properties assigned by `RegExp#exec` 657 | if (isArr) { 658 | if (hasOwnProperty.call(value, 'index')) { 659 | result.index = value.index; 660 | } 661 | if (hasOwnProperty.call(value, 'input')) { 662 | result.input = value.input; 663 | } 664 | } 665 | // exit for shallow clone 666 | if (!isDeep) { 667 | return result; 668 | } 669 | // add the source value to the stack of traversed objects 670 | // and associate it with its clone 671 | stackA.push(value); 672 | stackB.push(result); 673 | 674 | // recursively populate clone (susceptible to call stack limits) 675 | (isArr ? baseEach : forOwn)(value, function(objValue, key) { 676 | result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); 677 | }); 678 | 679 | if (initedStack) { 680 | releaseArray(stackA); 681 | releaseArray(stackB); 682 | } 683 | return result; 684 | } 685 | 686 | /** 687 | * The base implementation of `_.create` without support for assigning 688 | * properties to the created object. 689 | * 690 | * @private 691 | * @param {Object} prototype The object to inherit from. 692 | * @returns {Object} Returns the new object. 693 | */ 694 | function baseCreate(prototype, properties) { 695 | return isObject(prototype) ? nativeCreate(prototype) : {}; 696 | } 697 | // fallback for browsers without `Object.create` 698 | if (!nativeCreate) { 699 | baseCreate = (function() { 700 | function Object() {} 701 | return function(prototype) { 702 | if (isObject(prototype)) { 703 | Object.prototype = prototype; 704 | var result = new Object; 705 | Object.prototype = null; 706 | } 707 | return result || root.Object(); 708 | }; 709 | }()); 710 | } 711 | 712 | /** 713 | * The base implementation of `_.createCallback` without support for creating 714 | * "_.pluck" or "_.where" style callbacks. 715 | * 716 | * @private 717 | * @param {*} [func=identity] The value to convert to a callback. 718 | * @param {*} [thisArg] The `this` binding of the created callback. 719 | * @param {number} [argCount] The number of arguments the callback accepts. 720 | * @returns {Function} Returns a callback function. 721 | */ 722 | function baseCreateCallback(func, thisArg, argCount) { 723 | if (typeof func != 'function') { 724 | return identity; 725 | } 726 | // exit early for no `thisArg` or already bound by `Function#bind` 727 | if (typeof thisArg == 'undefined' || !('prototype' in func)) { 728 | return func; 729 | } 730 | var bindData = func.__bindData__; 731 | if (typeof bindData == 'undefined') { 732 | if (support.funcNames) { 733 | bindData = !func.name; 734 | } 735 | bindData = bindData || !support.funcDecomp; 736 | if (!bindData) { 737 | var source = fnToString.call(func); 738 | if (!support.funcNames) { 739 | bindData = !reFuncName.test(source); 740 | } 741 | if (!bindData) { 742 | // checks if `func` references the `this` keyword and stores the result 743 | bindData = reThis.test(source); 744 | setBindData(func, bindData); 745 | } 746 | } 747 | } 748 | // exit early if there are no `this` references or `func` is bound 749 | if (bindData === false || (bindData !== true && bindData[1] & 1)) { 750 | return func; 751 | } 752 | switch (argCount) { 753 | case 1: return function(value) { 754 | return func.call(thisArg, value); 755 | }; 756 | case 2: return function(a, b) { 757 | return func.call(thisArg, a, b); 758 | }; 759 | case 3: return function(value, index, collection) { 760 | return func.call(thisArg, value, index, collection); 761 | }; 762 | case 4: return function(accumulator, value, index, collection) { 763 | return func.call(thisArg, accumulator, value, index, collection); 764 | }; 765 | } 766 | return bind(func, thisArg); 767 | } 768 | 769 | /** 770 | * The base implementation of `createWrapper` that creates the wrapper and 771 | * sets its meta data. 772 | * 773 | * @private 774 | * @param {Array} bindData The bind data array. 775 | * @returns {Function} Returns the new function. 776 | */ 777 | function baseCreateWrapper(bindData) { 778 | var func = bindData[0], 779 | bitmask = bindData[1], 780 | partialArgs = bindData[2], 781 | partialRightArgs = bindData[3], 782 | thisArg = bindData[4], 783 | arity = bindData[5]; 784 | 785 | var isBind = bitmask & 1, 786 | isBindKey = bitmask & 2, 787 | isCurry = bitmask & 4, 788 | isCurryBound = bitmask & 8, 789 | key = func; 790 | 791 | function bound() { 792 | var thisBinding = isBind ? thisArg : this; 793 | if (partialArgs) { 794 | var args = slice(partialArgs); 795 | push.apply(args, arguments); 796 | } 797 | if (partialRightArgs || isCurry) { 798 | args || (args = slice(arguments)); 799 | if (partialRightArgs) { 800 | push.apply(args, partialRightArgs); 801 | } 802 | if (isCurry && args.length < arity) { 803 | bitmask |= 16 & ~32; 804 | return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); 805 | } 806 | } 807 | args || (args = arguments); 808 | if (isBindKey) { 809 | func = thisBinding[key]; 810 | } 811 | if (this instanceof bound) { 812 | thisBinding = baseCreate(func.prototype); 813 | var result = func.apply(thisBinding, args); 814 | return isObject(result) ? result : thisBinding; 815 | } 816 | return func.apply(thisBinding, args); 817 | } 818 | setBindData(bound, bindData); 819 | return bound; 820 | } 821 | 822 | /** 823 | * The base implementation of `_.isEqual`, without support for `thisArg` binding, 824 | * that allows partial "_.where" style comparisons. 825 | * 826 | * @private 827 | * @param {*} a The value to compare. 828 | * @param {*} b The other value to compare. 829 | * @param {Function} [callback] The function to customize comparing values. 830 | * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons. 831 | * @param {Array} [stackA=[]] Tracks traversed `a` objects. 832 | * @param {Array} [stackB=[]] Tracks traversed `b` objects. 833 | * @returns {boolean} Returns `true` if the values are equivalent, else `false`. 834 | */ 835 | function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { 836 | // used to indicate that when comparing objects, `a` has at least the properties of `b` 837 | if (callback) { 838 | var result = callback(a, b); 839 | if (typeof result != 'undefined') { 840 | return !!result; 841 | } 842 | } 843 | // exit early for identical values 844 | if (a === b) { 845 | // treat `+0` vs. `-0` as not equal 846 | return a !== 0 || (1 / a == 1 / b); 847 | } 848 | var type = typeof a, 849 | otherType = typeof b; 850 | 851 | // exit early for unlike primitive values 852 | if (a === a && 853 | !(a && objectTypes[type]) && 854 | !(b && objectTypes[otherType])) { 855 | return false; 856 | } 857 | // exit early for `null` and `undefined` avoiding ES3's Function#call behavior 858 | // http://es5.github.io/#x15.3.4.4 859 | if (a == null || b == null) { 860 | return a === b; 861 | } 862 | // compare [[Class]] names 863 | var className = toString.call(a), 864 | otherClass = toString.call(b); 865 | 866 | if (className == argsClass) { 867 | className = objectClass; 868 | } 869 | if (otherClass == argsClass) { 870 | otherClass = objectClass; 871 | } 872 | if (className != otherClass) { 873 | return false; 874 | } 875 | switch (className) { 876 | case boolClass: 877 | case dateClass: 878 | // coerce dates and booleans to numbers, dates to milliseconds and booleans 879 | // to `1` or `0` treating invalid dates coerced to `NaN` as not equal 880 | return +a == +b; 881 | 882 | case numberClass: 883 | // treat `NaN` vs. `NaN` as equal 884 | return (a != +a) 885 | ? b != +b 886 | // but treat `+0` vs. `-0` as not equal 887 | : (a == 0 ? (1 / a == 1 / b) : a == +b); 888 | 889 | case regexpClass: 890 | case stringClass: 891 | // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) 892 | // treat string primitives and their corresponding object instances as equal 893 | return a == String(b); 894 | } 895 | var isArr = className == arrayClass; 896 | if (!isArr) { 897 | // unwrap any `lodash` wrapped values 898 | var aWrapped = hasOwnProperty.call(a, '__wrapped__'), 899 | bWrapped = hasOwnProperty.call(b, '__wrapped__'); 900 | 901 | if (aWrapped || bWrapped) { 902 | return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB); 903 | } 904 | // exit for functions and DOM nodes 905 | if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) { 906 | return false; 907 | } 908 | // in older versions of Opera, `arguments` objects have `Array` constructors 909 | var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor, 910 | ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor; 911 | 912 | // non `Object` object instances with different constructors are not equal 913 | if (ctorA != ctorB && 914 | !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) && 915 | ('constructor' in a && 'constructor' in b) 916 | ) { 917 | return false; 918 | } 919 | } 920 | // assume cyclic structures are equal 921 | // the algorithm for detecting cyclic structures is adapted from ES 5.1 922 | // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3) 923 | var initedStack = !stackA; 924 | stackA || (stackA = getArray()); 925 | stackB || (stackB = getArray()); 926 | 927 | var length = stackA.length; 928 | while (length--) { 929 | if (stackA[length] == a) { 930 | return stackB[length] == b; 931 | } 932 | } 933 | var size = 0; 934 | result = true; 935 | 936 | // add `a` and `b` to the stack of traversed objects 937 | stackA.push(a); 938 | stackB.push(b); 939 | 940 | // recursively compare objects and arrays (susceptible to call stack limits) 941 | if (isArr) { 942 | // compare lengths to determine if a deep comparison is necessary 943 | length = a.length; 944 | size = b.length; 945 | result = size == length; 946 | 947 | if (result || isWhere) { 948 | // deep compare the contents, ignoring non-numeric properties 949 | while (size--) { 950 | var index = length, 951 | value = b[size]; 952 | 953 | if (isWhere) { 954 | while (index--) { 955 | if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) { 956 | break; 957 | } 958 | } 959 | } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) { 960 | break; 961 | } 962 | } 963 | } 964 | } 965 | else { 966 | // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` 967 | // which, in this case, is more costly 968 | forIn(b, function(value, key, b) { 969 | if (hasOwnProperty.call(b, key)) { 970 | // count the number of properties. 971 | size++; 972 | // deep compare each property value. 973 | return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB)); 974 | } 975 | }); 976 | 977 | if (result && !isWhere) { 978 | // ensure both objects have the same number of properties 979 | forIn(a, function(value, key, a) { 980 | if (hasOwnProperty.call(a, key)) { 981 | // `size` will be `-1` if `a` has more properties than `b` 982 | return (result = --size > -1); 983 | } 984 | }); 985 | } 986 | } 987 | stackA.pop(); 988 | stackB.pop(); 989 | 990 | if (initedStack) { 991 | releaseArray(stackA); 992 | releaseArray(stackB); 993 | } 994 | return result; 995 | } 996 | 997 | /** 998 | * The base implementation of `_.random` without argument juggling or support 999 | * for returning floating-point numbers. 1000 | * 1001 | * @private 1002 | * @param {number} min The minimum possible value. 1003 | * @param {number} max The maximum possible value. 1004 | * @returns {number} Returns a random number. 1005 | */ 1006 | function baseRandom(min, max) { 1007 | return min + floor(nativeRandom() * (max - min + 1)); 1008 | } 1009 | 1010 | /** 1011 | * Creates a function that, when called, either curries or invokes `func` 1012 | * with an optional `this` binding and partially applied arguments. 1013 | * 1014 | * @private 1015 | * @param {Function|string} func The function or method name to reference. 1016 | * @param {number} bitmask The bitmask of method flags to compose. 1017 | * The bitmask may be composed of the following flags: 1018 | * 1 - `_.bind` 1019 | * 2 - `_.bindKey` 1020 | * 4 - `_.curry` 1021 | * 8 - `_.curry` (bound) 1022 | * 16 - `_.partial` 1023 | * 32 - `_.partialRight` 1024 | * @param {Array} [partialArgs] An array of arguments to prepend to those 1025 | * provided to the new function. 1026 | * @param {Array} [partialRightArgs] An array of arguments to append to those 1027 | * provided to the new function. 1028 | * @param {*} [thisArg] The `this` binding of `func`. 1029 | * @param {number} [arity] The arity of `func`. 1030 | * @returns {Function} Returns the new function. 1031 | */ 1032 | function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { 1033 | var isBind = bitmask & 1, 1034 | isBindKey = bitmask & 2, 1035 | isCurry = bitmask & 4, 1036 | isCurryBound = bitmask & 8, 1037 | isPartial = bitmask & 16, 1038 | isPartialRight = bitmask & 32; 1039 | 1040 | if (!isBindKey && !isFunction(func)) { 1041 | throw new TypeError; 1042 | } 1043 | if (isPartial && !partialArgs.length) { 1044 | bitmask &= ~16; 1045 | isPartial = partialArgs = false; 1046 | } 1047 | if (isPartialRight && !partialRightArgs.length) { 1048 | bitmask &= ~32; 1049 | isPartialRight = partialRightArgs = false; 1050 | } 1051 | var bindData = func && func.__bindData__; 1052 | if (bindData && bindData !== true) { 1053 | // clone `bindData` 1054 | bindData = slice(bindData); 1055 | if (bindData[2]) { 1056 | bindData[2] = slice(bindData[2]); 1057 | } 1058 | if (bindData[3]) { 1059 | bindData[3] = slice(bindData[3]); 1060 | } 1061 | // set `thisBinding` is not previously bound 1062 | if (isBind && !(bindData[1] & 1)) { 1063 | bindData[4] = thisArg; 1064 | } 1065 | // set if previously bound but not currently (subsequent curried functions) 1066 | if (!isBind && bindData[1] & 1) { 1067 | bitmask |= 8; 1068 | } 1069 | // set curried arity if not yet set 1070 | if (isCurry && !(bindData[1] & 4)) { 1071 | bindData[5] = arity; 1072 | } 1073 | // append partial left arguments 1074 | if (isPartial) { 1075 | push.apply(bindData[2] || (bindData[2] = []), partialArgs); 1076 | } 1077 | // append partial right arguments 1078 | if (isPartialRight) { 1079 | unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); 1080 | } 1081 | // merge flags 1082 | bindData[1] |= bitmask; 1083 | return createWrapper.apply(null, bindData); 1084 | } 1085 | // fast path for `_.bind` 1086 | var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; 1087 | return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); 1088 | } 1089 | 1090 | /** 1091 | * Creates compiled iteration functions. 1092 | * 1093 | * @private 1094 | * @param {...Object} [options] The compile options object(s). 1095 | * @param {string} [options.array] Code to determine if the iterable is an array or array-like. 1096 | * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. 1097 | * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration. 1098 | * @param {string} [options.args] A comma separated string of iteration function arguments. 1099 | * @param {string} [options.top] Code to execute before the iteration branches. 1100 | * @param {string} [options.loop] Code to execute in the object loop. 1101 | * @param {string} [options.bottom] Code to execute after the iteration branches. 1102 | * @returns {Function} Returns the compiled function. 1103 | */ 1104 | function createIterator() { 1105 | // data properties 1106 | iteratorData.shadowedProps = shadowedProps; 1107 | 1108 | // iterator options 1109 | iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = ''; 1110 | iteratorData.init = 'iterable'; 1111 | iteratorData.useHas = true; 1112 | 1113 | // merge options into a template data object 1114 | for (var object, index = 0; object = arguments[index]; index++) { 1115 | for (var key in object) { 1116 | iteratorData[key] = object[key]; 1117 | } 1118 | } 1119 | var args = iteratorData.args; 1120 | iteratorData.firstArg = /^[^,]+/.exec(args)[0]; 1121 | 1122 | // create the function factory 1123 | var factory = Function( 1124 | 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' + 1125 | 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' + 1126 | 'objectTypes, nonEnumProps, stringClass, stringProto, toString', 1127 | 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}' 1128 | ); 1129 | 1130 | // return the compiled function 1131 | return factory( 1132 | baseCreateCallback, errorClass, errorProto, hasOwnProperty, 1133 | indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto, 1134 | objectTypes, nonEnumProps, stringClass, stringProto, toString 1135 | ); 1136 | } 1137 | 1138 | /** 1139 | * Checks if `value` is a native function. 1140 | * 1141 | * @private 1142 | * @param {*} value The value to check. 1143 | * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. 1144 | */ 1145 | function isNative(value) { 1146 | return typeof value == 'function' && reNative.test(value); 1147 | } 1148 | 1149 | /** 1150 | * Sets `this` binding data on a given function. 1151 | * 1152 | * @private 1153 | * @param {Function} func The function to set data on. 1154 | * @param {Array} value The data array to set. 1155 | */ 1156 | var setBindData = !defineProperty ? noop : function(func, value) { 1157 | descriptor.value = value; 1158 | defineProperty(func, '__bindData__', descriptor); 1159 | }; 1160 | 1161 | /*--------------------------------------------------------------------------*/ 1162 | 1163 | /** 1164 | * Checks if `value` is an `arguments` object. 1165 | * 1166 | * @static 1167 | * @memberOf _ 1168 | * @category Objects 1169 | * @param {*} value The value to check. 1170 | * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. 1171 | * @example 1172 | * 1173 | * (function() { return _.isArguments(arguments); })(1, 2, 3); 1174 | * // => true 1175 | * 1176 | * _.isArguments([1, 2, 3]); 1177 | * // => false 1178 | */ 1179 | function isArguments(value) { 1180 | return value && typeof value == 'object' && typeof value.length == 'number' && 1181 | toString.call(value) == argsClass || false; 1182 | } 1183 | // fallback for browsers that can't detect `arguments` objects by [[Class]] 1184 | if (!support.argsClass) { 1185 | isArguments = function(value) { 1186 | return value && typeof value == 'object' && typeof value.length == 'number' && 1187 | hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; 1188 | }; 1189 | } 1190 | 1191 | /** 1192 | * Checks if `value` is an array. 1193 | * 1194 | * @static 1195 | * @memberOf _ 1196 | * @type Function 1197 | * @category Objects 1198 | * @param {*} value The value to check. 1199 | * @returns {boolean} Returns `true` if the `value` is an array, else `false`. 1200 | * @example 1201 | * 1202 | * (function() { return _.isArray(arguments); })(); 1203 | * // => false 1204 | * 1205 | * _.isArray([1, 2, 3]); 1206 | * // => true 1207 | */ 1208 | var isArray = nativeIsArray || function(value) { 1209 | return value && typeof value == 'object' && typeof value.length == 'number' && 1210 | toString.call(value) == arrayClass || false; 1211 | }; 1212 | 1213 | /** 1214 | * A fallback implementation of `Object.keys` which produces an array of the 1215 | * given object's own enumerable property names. 1216 | * 1217 | * @private 1218 | * @type Function 1219 | * @param {Object} object The object to inspect. 1220 | * @returns {Array} Returns an array of property names. 1221 | */ 1222 | var shimKeys = createIterator({ 1223 | 'args': 'object', 1224 | 'init': '[]', 1225 | 'top': 'if (!(objectTypes[typeof object])) return result', 1226 | 'loop': 'result.push(index)' 1227 | }); 1228 | 1229 | /** 1230 | * Creates an array composed of the own enumerable property names of an object. 1231 | * 1232 | * @static 1233 | * @memberOf _ 1234 | * @category Objects 1235 | * @param {Object} object The object to inspect. 1236 | * @returns {Array} Returns an array of property names. 1237 | * @example 1238 | * 1239 | * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); 1240 | * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) 1241 | */ 1242 | var keys = !nativeKeys ? shimKeys : function(object) { 1243 | if (!isObject(object)) { 1244 | return []; 1245 | } 1246 | if ((support.enumPrototypes && typeof object == 'function') || 1247 | (support.nonEnumArgs && object.length && isArguments(object))) { 1248 | return shimKeys(object); 1249 | } 1250 | return nativeKeys(object); 1251 | }; 1252 | 1253 | /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ 1254 | var eachIteratorOptions = { 1255 | 'args': 'collection, callback, thisArg', 1256 | 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)", 1257 | 'array': "typeof length == 'number'", 1258 | 'keys': keys, 1259 | 'loop': 'if (callback(iterable[index], index, collection) === false) return result' 1260 | }; 1261 | 1262 | /** Reusable iterator options for `assign` and `defaults` */ 1263 | var defaultsIteratorOptions = { 1264 | 'args': 'object, source, guard', 1265 | 'top': 1266 | 'var args = arguments,\n' + 1267 | ' argsIndex = 0,\n' + 1268 | " argsLength = typeof guard == 'number' ? 2 : args.length;\n" + 1269 | 'while (++argsIndex < argsLength) {\n' + 1270 | ' iterable = args[argsIndex];\n' + 1271 | ' if (iterable && objectTypes[typeof iterable]) {', 1272 | 'keys': keys, 1273 | 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]", 1274 | 'bottom': ' }\n}' 1275 | }; 1276 | 1277 | /** Reusable iterator options for `forIn` and `forOwn` */ 1278 | var forOwnIteratorOptions = { 1279 | 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, 1280 | 'array': false 1281 | }; 1282 | 1283 | /** 1284 | * A function compiled to iterate `arguments` objects, arrays, objects, and 1285 | * strings consistenly across environments, executing the callback for each 1286 | * element in the collection. The callback is bound to `thisArg` and invoked 1287 | * with three arguments; (value, index|key, collection). Callbacks may exit 1288 | * iteration early by explicitly returning `false`. 1289 | * 1290 | * @private 1291 | * @type Function 1292 | * @param {Array|Object|string} collection The collection to iterate over. 1293 | * @param {Function} [callback=identity] The function called per iteration. 1294 | * @param {*} [thisArg] The `this` binding of `callback`. 1295 | * @returns {Array|Object|string} Returns `collection`. 1296 | */ 1297 | var baseEach = createIterator(eachIteratorOptions); 1298 | 1299 | /*--------------------------------------------------------------------------*/ 1300 | 1301 | /** 1302 | * Assigns own enumerable properties of source object(s) to the destination 1303 | * object. Subsequent sources will overwrite property assignments of previous 1304 | * sources. If a callback is provided it will be executed to produce the 1305 | * assigned values. The callback is bound to `thisArg` and invoked with two 1306 | * arguments; (objectValue, sourceValue). 1307 | * 1308 | * @static 1309 | * @memberOf _ 1310 | * @type Function 1311 | * @alias extend 1312 | * @category Objects 1313 | * @param {Object} object The destination object. 1314 | * @param {...Object} [source] The source objects. 1315 | * @param {Function} [callback] The function to customize assigning values. 1316 | * @param {*} [thisArg] The `this` binding of `callback`. 1317 | * @returns {Object} Returns the destination object. 1318 | * @example 1319 | * 1320 | * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); 1321 | * // => { 'name': 'fred', 'employer': 'slate' } 1322 | * 1323 | * var defaults = _.partialRight(_.assign, function(a, b) { 1324 | * return typeof a == 'undefined' ? b : a; 1325 | * }); 1326 | * 1327 | * var object = { 'name': 'barney' }; 1328 | * defaults(object, { 'name': 'fred', 'employer': 'slate' }); 1329 | * // => { 'name': 'barney', 'employer': 'slate' } 1330 | */ 1331 | var assign = createIterator(defaultsIteratorOptions, { 1332 | 'top': 1333 | defaultsIteratorOptions.top.replace(';', 1334 | ';\n' + 1335 | "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" + 1336 | ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' + 1337 | "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" + 1338 | ' callback = args[--argsLength];\n' + 1339 | '}' 1340 | ), 1341 | 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]' 1342 | }); 1343 | 1344 | /** 1345 | * Creates a clone of `value`. If `isDeep` is `true` nested objects will also 1346 | * be cloned, otherwise they will be assigned by reference. If a callback 1347 | * is provided it will be executed to produce the cloned values. If the 1348 | * callback returns `undefined` cloning will be handled by the method instead. 1349 | * The callback is bound to `thisArg` and invoked with one argument; (value). 1350 | * 1351 | * @static 1352 | * @memberOf _ 1353 | * @category Objects 1354 | * @param {*} value The value to clone. 1355 | * @param {boolean} [isDeep=false] Specify a deep clone. 1356 | * @param {Function} [callback] The function to customize cloning values. 1357 | * @param {*} [thisArg] The `this` binding of `callback`. 1358 | * @returns {*} Returns the cloned value. 1359 | * @example 1360 | * 1361 | * var characters = [ 1362 | * { 'name': 'barney', 'age': 36 }, 1363 | * { 'name': 'fred', 'age': 40 } 1364 | * ]; 1365 | * 1366 | * var shallow = _.clone(characters); 1367 | * shallow[0] === characters[0]; 1368 | * // => true 1369 | * 1370 | * var deep = _.clone(characters, true); 1371 | * deep[0] === characters[0]; 1372 | * // => false 1373 | * 1374 | * _.mixin({ 1375 | * 'clone': _.partialRight(_.clone, function(value) { 1376 | * return _.isElement(value) ? value.cloneNode(false) : undefined; 1377 | * }) 1378 | * }); 1379 | * 1380 | * var clone = _.clone(document.body); 1381 | * clone.childNodes.length; 1382 | * // => 0 1383 | */ 1384 | function clone(value, isDeep, callback, thisArg) { 1385 | // allows working with "Collections" methods without using their `index` 1386 | // and `collection` arguments for `isDeep` and `callback` 1387 | if (typeof isDeep != 'boolean' && isDeep != null) { 1388 | thisArg = callback; 1389 | callback = isDeep; 1390 | isDeep = false; 1391 | } 1392 | return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); 1393 | } 1394 | 1395 | /** 1396 | * Iterates over own and inherited enumerable properties of an object, 1397 | * executing the callback for each property. The callback is bound to `thisArg` 1398 | * and invoked with three arguments; (value, key, object). Callbacks may exit 1399 | * iteration early by explicitly returning `false`. 1400 | * 1401 | * @static 1402 | * @memberOf _ 1403 | * @type Function 1404 | * @category Objects 1405 | * @param {Object} object The object to iterate over. 1406 | * @param {Function} [callback=identity] The function called per iteration. 1407 | * @param {*} [thisArg] The `this` binding of `callback`. 1408 | * @returns {Object} Returns `object`. 1409 | * @example 1410 | * 1411 | * function Shape() { 1412 | * this.x = 0; 1413 | * this.y = 0; 1414 | * } 1415 | * 1416 | * Shape.prototype.move = function(x, y) { 1417 | * this.x += x; 1418 | * this.y += y; 1419 | * }; 1420 | * 1421 | * _.forIn(new Shape, function(value, key) { 1422 | * console.log(key); 1423 | * }); 1424 | * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) 1425 | */ 1426 | var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { 1427 | 'useHas': false 1428 | }); 1429 | 1430 | /** 1431 | * Iterates over own enumerable properties of an object, executing the callback 1432 | * for each property. The callback is bound to `thisArg` and invoked with three 1433 | * arguments; (value, key, object). Callbacks may exit iteration early by 1434 | * explicitly returning `false`. 1435 | * 1436 | * @static 1437 | * @memberOf _ 1438 | * @type Function 1439 | * @category Objects 1440 | * @param {Object} object The object to iterate over. 1441 | * @param {Function} [callback=identity] The function called per iteration. 1442 | * @param {*} [thisArg] The `this` binding of `callback`. 1443 | * @returns {Object} Returns `object`. 1444 | * @example 1445 | * 1446 | * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { 1447 | * console.log(key); 1448 | * }); 1449 | * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) 1450 | */ 1451 | var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); 1452 | 1453 | /** 1454 | * Checks if `value` is a function. 1455 | * 1456 | * @static 1457 | * @memberOf _ 1458 | * @category Objects 1459 | * @param {*} value The value to check. 1460 | * @returns {boolean} Returns `true` if the `value` is a function, else `false`. 1461 | * @example 1462 | * 1463 | * _.isFunction(_); 1464 | * // => true 1465 | */ 1466 | function isFunction(value) { 1467 | return typeof value == 'function'; 1468 | } 1469 | // fallback for older versions of Chrome and Safari 1470 | if (isFunction(/x/)) { 1471 | isFunction = function(value) { 1472 | return typeof value == 'function' && toString.call(value) == funcClass; 1473 | }; 1474 | } 1475 | 1476 | /** 1477 | * Checks if `value` is the language type of Object. 1478 | * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 1479 | * 1480 | * @static 1481 | * @memberOf _ 1482 | * @category Objects 1483 | * @param {*} value The value to check. 1484 | * @returns {boolean} Returns `true` if the `value` is an object, else `false`. 1485 | * @example 1486 | * 1487 | * _.isObject({}); 1488 | * // => true 1489 | * 1490 | * _.isObject([1, 2, 3]); 1491 | * // => true 1492 | * 1493 | * _.isObject(1); 1494 | * // => false 1495 | */ 1496 | function isObject(value) { 1497 | // check if the value is the ECMAScript language type of Object 1498 | // http://es5.github.io/#x8 1499 | // and avoid a V8 bug 1500 | // http://code.google.com/p/v8/issues/detail?id=2291 1501 | return !!(value && objectTypes[typeof value]); 1502 | } 1503 | 1504 | /** 1505 | * Checks if `value` is a string. 1506 | * 1507 | * @static 1508 | * @memberOf _ 1509 | * @category Objects 1510 | * @param {*} value The value to check. 1511 | * @returns {boolean} Returns `true` if the `value` is a string, else `false`. 1512 | * @example 1513 | * 1514 | * _.isString('fred'); 1515 | * // => true 1516 | */ 1517 | function isString(value) { 1518 | return typeof value == 'string' || 1519 | value && typeof value == 'object' && toString.call(value) == stringClass || false; 1520 | } 1521 | 1522 | /*--------------------------------------------------------------------------*/ 1523 | 1524 | /** 1525 | * Iterates over elements of a collection, returning an array of all elements 1526 | * the callback returns truey for. The callback is bound to `thisArg` and 1527 | * invoked with three arguments; (value, index|key, collection). 1528 | * 1529 | * If a property name is provided for `callback` the created "_.pluck" style 1530 | * callback will return the property value of the given element. 1531 | * 1532 | * If an object is provided for `callback` the created "_.where" style callback 1533 | * will return `true` for elements that have the properties of the given object, 1534 | * else `false`. 1535 | * 1536 | * @static 1537 | * @memberOf _ 1538 | * @alias select 1539 | * @category Collections 1540 | * @param {Array|Object|string} collection The collection to iterate over. 1541 | * @param {Function|Object|string} [callback=identity] The function called 1542 | * per iteration. If a property name or object is provided it will be used 1543 | * to create a "_.pluck" or "_.where" style callback, respectively. 1544 | * @param {*} [thisArg] The `this` binding of `callback`. 1545 | * @returns {Array} Returns a new array of elements that passed the callback check. 1546 | * @example 1547 | * 1548 | * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); 1549 | * // => [2, 4, 6] 1550 | * 1551 | * var characters = [ 1552 | * { 'name': 'barney', 'age': 36, 'blocked': false }, 1553 | * { 'name': 'fred', 'age': 40, 'blocked': true } 1554 | * ]; 1555 | * 1556 | * // using "_.pluck" callback shorthand 1557 | * _.filter(characters, 'blocked'); 1558 | * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] 1559 | * 1560 | * // using "_.where" callback shorthand 1561 | * _.filter(characters, { 'age': 36 }); 1562 | * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] 1563 | */ 1564 | function filter(collection, callback, thisArg) { 1565 | var result = []; 1566 | callback = lodash.createCallback(callback, thisArg, 3); 1567 | 1568 | if (isArray(collection)) { 1569 | var index = -1, 1570 | length = collection.length; 1571 | 1572 | while (++index < length) { 1573 | var value = collection[index]; 1574 | if (callback(value, index, collection)) { 1575 | result.push(value); 1576 | } 1577 | } 1578 | } else { 1579 | baseEach(collection, function(value, index, collection) { 1580 | if (callback(value, index, collection)) { 1581 | result.push(value); 1582 | } 1583 | }); 1584 | } 1585 | return result; 1586 | } 1587 | 1588 | /** 1589 | * Iterates over elements of a collection, executing the callback for each 1590 | * element. The callback is bound to `thisArg` and invoked with three arguments; 1591 | * (value, index|key, collection). Callbacks may exit iteration early by 1592 | * explicitly returning `false`. 1593 | * 1594 | * Note: As with other "Collections" methods, objects with a `length` property 1595 | * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` 1596 | * may be used for object iteration. 1597 | * 1598 | * @static 1599 | * @memberOf _ 1600 | * @alias each 1601 | * @category Collections 1602 | * @param {Array|Object|string} collection The collection to iterate over. 1603 | * @param {Function} [callback=identity] The function called per iteration. 1604 | * @param {*} [thisArg] The `this` binding of `callback`. 1605 | * @returns {Array|Object|string} Returns `collection`. 1606 | * @example 1607 | * 1608 | * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); 1609 | * // => logs each number and returns '1,2,3' 1610 | * 1611 | * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); 1612 | * // => logs each number and returns the object (property order is not guaranteed across environments) 1613 | */ 1614 | function forEach(collection, callback, thisArg) { 1615 | if (callback && typeof thisArg == 'undefined' && isArray(collection)) { 1616 | var index = -1, 1617 | length = collection.length; 1618 | 1619 | while (++index < length) { 1620 | if (callback(collection[index], index, collection) === false) { 1621 | break; 1622 | } 1623 | } 1624 | } else { 1625 | baseEach(collection, callback, thisArg); 1626 | } 1627 | return collection; 1628 | } 1629 | 1630 | /** 1631 | * Creates an array of values by running each element in the collection 1632 | * through the callback. The callback is bound to `thisArg` and invoked with 1633 | * three arguments; (value, index|key, collection). 1634 | * 1635 | * If a property name is provided for `callback` the created "_.pluck" style 1636 | * callback will return the property value of the given element. 1637 | * 1638 | * If an object is provided for `callback` the created "_.where" style callback 1639 | * will return `true` for elements that have the properties of the given object, 1640 | * else `false`. 1641 | * 1642 | * @static 1643 | * @memberOf _ 1644 | * @alias collect 1645 | * @category Collections 1646 | * @param {Array|Object|string} collection The collection to iterate over. 1647 | * @param {Function|Object|string} [callback=identity] The function called 1648 | * per iteration. If a property name or object is provided it will be used 1649 | * to create a "_.pluck" or "_.where" style callback, respectively. 1650 | * @param {*} [thisArg] The `this` binding of `callback`. 1651 | * @returns {Array} Returns a new array of the results of each `callback` execution. 1652 | * @example 1653 | * 1654 | * _.map([1, 2, 3], function(num) { return num * 3; }); 1655 | * // => [3, 6, 9] 1656 | * 1657 | * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); 1658 | * // => [3, 6, 9] (property order is not guaranteed across environments) 1659 | * 1660 | * var characters = [ 1661 | * { 'name': 'barney', 'age': 36 }, 1662 | * { 'name': 'fred', 'age': 40 } 1663 | * ]; 1664 | * 1665 | * // using "_.pluck" callback shorthand 1666 | * _.map(characters, 'name'); 1667 | * // => ['barney', 'fred'] 1668 | */ 1669 | function map(collection, callback, thisArg) { 1670 | var index = -1, 1671 | length = collection ? collection.length : 0, 1672 | result = Array(typeof length == 'number' ? length : 0); 1673 | 1674 | callback = lodash.createCallback(callback, thisArg, 3); 1675 | if (isArray(collection)) { 1676 | while (++index < length) { 1677 | result[index] = callback(collection[index], index, collection); 1678 | } 1679 | } else { 1680 | baseEach(collection, function(value, key, collection) { 1681 | result[++index] = callback(value, key, collection); 1682 | }); 1683 | } 1684 | return result; 1685 | } 1686 | 1687 | /** 1688 | * Reduces a collection to a value which is the accumulated result of running 1689 | * each element in the collection through the callback, where each successive 1690 | * callback execution consumes the return value of the previous execution. If 1691 | * `accumulator` is not provided the first element of the collection will be 1692 | * used as the initial `accumulator` value. The callback is bound to `thisArg` 1693 | * and invoked with four arguments; (accumulator, value, index|key, collection). 1694 | * 1695 | * @static 1696 | * @memberOf _ 1697 | * @alias foldl, inject 1698 | * @category Collections 1699 | * @param {Array|Object|string} collection The collection to iterate over. 1700 | * @param {Function} [callback=identity] The function called per iteration. 1701 | * @param {*} [accumulator] Initial value of the accumulator. 1702 | * @param {*} [thisArg] The `this` binding of `callback`. 1703 | * @returns {*} Returns the accumulated value. 1704 | * @example 1705 | * 1706 | * var sum = _.reduce([1, 2, 3], function(sum, num) { 1707 | * return sum + num; 1708 | * }); 1709 | * // => 6 1710 | * 1711 | * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { 1712 | * result[key] = num * 3; 1713 | * return result; 1714 | * }, {}); 1715 | * // => { 'a': 3, 'b': 6, 'c': 9 } 1716 | */ 1717 | function reduce(collection, callback, accumulator, thisArg) { 1718 | var noaccum = arguments.length < 3; 1719 | callback = lodash.createCallback(callback, thisArg, 4); 1720 | 1721 | if (isArray(collection)) { 1722 | var index = -1, 1723 | length = collection.length; 1724 | 1725 | if (noaccum) { 1726 | accumulator = collection[++index]; 1727 | } 1728 | while (++index < length) { 1729 | accumulator = callback(accumulator, collection[index], index, collection); 1730 | } 1731 | } else { 1732 | baseEach(collection, function(value, index, collection) { 1733 | accumulator = noaccum 1734 | ? (noaccum = false, value) 1735 | : callback(accumulator, value, index, collection) 1736 | }); 1737 | } 1738 | return accumulator; 1739 | } 1740 | 1741 | /** 1742 | * Checks if the callback returns a truey value for **any** element of a 1743 | * collection. The function returns as soon as it finds a passing value and 1744 | * does not iterate over the entire collection. The callback is bound to 1745 | * `thisArg` and invoked with three arguments; (value, index|key, collection). 1746 | * 1747 | * If a property name is provided for `callback` the created "_.pluck" style 1748 | * callback will return the property value of the given element. 1749 | * 1750 | * If an object is provided for `callback` the created "_.where" style callback 1751 | * will return `true` for elements that have the properties of the given object, 1752 | * else `false`. 1753 | * 1754 | * @static 1755 | * @memberOf _ 1756 | * @alias any 1757 | * @category Collections 1758 | * @param {Array|Object|string} collection The collection to iterate over. 1759 | * @param {Function|Object|string} [callback=identity] The function called 1760 | * per iteration. If a property name or object is provided it will be used 1761 | * to create a "_.pluck" or "_.where" style callback, respectively. 1762 | * @param {*} [thisArg] The `this` binding of `callback`. 1763 | * @returns {boolean} Returns `true` if any element passed the callback check, 1764 | * else `false`. 1765 | * @example 1766 | * 1767 | * _.some([null, 0, 'yes', false], Boolean); 1768 | * // => true 1769 | * 1770 | * var characters = [ 1771 | * { 'name': 'barney', 'age': 36, 'blocked': false }, 1772 | * { 'name': 'fred', 'age': 40, 'blocked': true } 1773 | * ]; 1774 | * 1775 | * // using "_.pluck" callback shorthand 1776 | * _.some(characters, 'blocked'); 1777 | * // => true 1778 | * 1779 | * // using "_.where" callback shorthand 1780 | * _.some(characters, { 'age': 1 }); 1781 | * // => false 1782 | */ 1783 | function some(collection, callback, thisArg) { 1784 | var result; 1785 | callback = lodash.createCallback(callback, thisArg, 3); 1786 | 1787 | if (isArray(collection)) { 1788 | var index = -1, 1789 | length = collection.length; 1790 | 1791 | while (++index < length) { 1792 | if ((result = callback(collection[index], index, collection))) { 1793 | break; 1794 | } 1795 | } 1796 | } else { 1797 | baseEach(collection, function(value, index, collection) { 1798 | return !(result = callback(value, index, collection)); 1799 | }); 1800 | } 1801 | return !!result; 1802 | } 1803 | 1804 | /*--------------------------------------------------------------------------*/ 1805 | 1806 | /** 1807 | * Creates a function that, when called, invokes `func` with the `this` 1808 | * binding of `thisArg` and prepends any additional `bind` arguments to those 1809 | * provided to the bound function. 1810 | * 1811 | * @static 1812 | * @memberOf _ 1813 | * @category Functions 1814 | * @param {Function} func The function to bind. 1815 | * @param {*} [thisArg] The `this` binding of `func`. 1816 | * @param {...*} [arg] Arguments to be partially applied. 1817 | * @returns {Function} Returns the new bound function. 1818 | * @example 1819 | * 1820 | * var func = function(greeting) { 1821 | * return greeting + ' ' + this.name; 1822 | * }; 1823 | * 1824 | * func = _.bind(func, { 'name': 'fred' }, 'hi'); 1825 | * func(); 1826 | * // => 'hi fred' 1827 | */ 1828 | function bind(func, thisArg) { 1829 | return arguments.length > 2 1830 | ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) 1831 | : createWrapper(func, 1, null, null, thisArg); 1832 | } 1833 | 1834 | /*--------------------------------------------------------------------------*/ 1835 | 1836 | /** 1837 | * Produces a callback bound to an optional `thisArg`. If `func` is a property 1838 | * name the created callback will return the property value for a given element. 1839 | * If `func` is an object the created callback will return `true` for elements 1840 | * that contain the equivalent object properties, otherwise it will return `false`. 1841 | * 1842 | * @static 1843 | * @memberOf _ 1844 | * @category Utilities 1845 | * @param {*} [func=identity] The value to convert to a callback. 1846 | * @param {*} [thisArg] The `this` binding of the created callback. 1847 | * @param {number} [argCount] The number of arguments the callback accepts. 1848 | * @returns {Function} Returns a callback function. 1849 | * @example 1850 | * 1851 | * var characters = [ 1852 | * { 'name': 'barney', 'age': 36 }, 1853 | * { 'name': 'fred', 'age': 40 } 1854 | * ]; 1855 | * 1856 | * // wrap to create custom callback shorthands 1857 | * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { 1858 | * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); 1859 | * return !match ? func(callback, thisArg) : function(object) { 1860 | * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; 1861 | * }; 1862 | * }); 1863 | * 1864 | * _.filter(characters, 'age__gt38'); 1865 | * // => [{ 'name': 'fred', 'age': 40 }] 1866 | */ 1867 | function createCallback(func, thisArg, argCount) { 1868 | var type = typeof func; 1869 | if (func == null || type == 'function') { 1870 | return baseCreateCallback(func, thisArg, argCount); 1871 | } 1872 | // handle "_.pluck" style callback shorthands 1873 | if (type != 'object') { 1874 | return property(func); 1875 | } 1876 | var props = keys(func), 1877 | key = props[0], 1878 | a = func[key]; 1879 | 1880 | // handle "_.where" style callback shorthands 1881 | if (props.length == 1 && a === a && !isObject(a)) { 1882 | // fast path the common case of providing an object with a single 1883 | // property containing a primitive value 1884 | return function(object) { 1885 | var b = object[key]; 1886 | return a === b && (a !== 0 || (1 / a == 1 / b)); 1887 | }; 1888 | } 1889 | return function(object) { 1890 | var length = props.length, 1891 | result = false; 1892 | 1893 | while (length--) { 1894 | if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) { 1895 | break; 1896 | } 1897 | } 1898 | return result; 1899 | }; 1900 | } 1901 | 1902 | /** 1903 | * This method returns the first argument provided to it. 1904 | * 1905 | * @static 1906 | * @memberOf _ 1907 | * @category Utilities 1908 | * @param {*} value Any value. 1909 | * @returns {*} Returns `value`. 1910 | * @example 1911 | * 1912 | * var object = { 'name': 'fred' }; 1913 | * _.identity(object) === object; 1914 | * // => true 1915 | */ 1916 | function identity(value) { 1917 | return value; 1918 | } 1919 | 1920 | /** 1921 | * A no-operation function. 1922 | * 1923 | * @static 1924 | * @memberOf _ 1925 | * @category Utilities 1926 | * @example 1927 | * 1928 | * var object = { 'name': 'fred' }; 1929 | * _.noop(object) === undefined; 1930 | * // => true 1931 | */ 1932 | function noop() { 1933 | // no operation performed 1934 | } 1935 | 1936 | /** 1937 | * Creates a "_.pluck" style function, which returns the `key` value of a 1938 | * given object. 1939 | * 1940 | * @static 1941 | * @memberOf _ 1942 | * @category Utilities 1943 | * @param {string} key The name of the property to retrieve. 1944 | * @returns {Function} Returns the new function. 1945 | * @example 1946 | * 1947 | * var characters = [ 1948 | * { 'name': 'fred', 'age': 40 }, 1949 | * { 'name': 'barney', 'age': 36 } 1950 | * ]; 1951 | * 1952 | * var getName = _.property('name'); 1953 | * 1954 | * _.map(characters, getName); 1955 | * // => ['barney', 'fred'] 1956 | * 1957 | * _.sortBy(characters, getName); 1958 | * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] 1959 | */ 1960 | function property(key) { 1961 | return function(object) { 1962 | return object[key]; 1963 | }; 1964 | } 1965 | 1966 | /** 1967 | * Produces a random number between `min` and `max` (inclusive). If only one 1968 | * argument is provided a number between `0` and the given number will be 1969 | * returned. If `floating` is truey or either `min` or `max` are floats a 1970 | * floating-point number will be returned instead of an integer. 1971 | * 1972 | * @static 1973 | * @memberOf _ 1974 | * @category Utilities 1975 | * @param {number} [min=0] The minimum possible value. 1976 | * @param {number} [max=1] The maximum possible value. 1977 | * @param {boolean} [floating=false] Specify returning a floating-point number. 1978 | * @returns {number} Returns a random number. 1979 | * @example 1980 | * 1981 | * _.random(0, 5); 1982 | * // => an integer between 0 and 5 1983 | * 1984 | * _.random(5); 1985 | * // => also an integer between 0 and 5 1986 | * 1987 | * _.random(5, true); 1988 | * // => a floating-point number between 0 and 5 1989 | * 1990 | * _.random(1.2, 5.2); 1991 | * // => a floating-point number between 1.2 and 5.2 1992 | */ 1993 | function random(min, max, floating) { 1994 | var noMin = min == null, 1995 | noMax = max == null; 1996 | 1997 | if (floating == null) { 1998 | if (typeof min == 'boolean' && noMax) { 1999 | floating = min; 2000 | min = 1; 2001 | } 2002 | else if (!noMax && typeof max == 'boolean') { 2003 | floating = max; 2004 | noMax = true; 2005 | } 2006 | } 2007 | if (noMin && noMax) { 2008 | max = 1; 2009 | } 2010 | min = +min || 0; 2011 | if (noMax) { 2012 | max = min; 2013 | min = 0; 2014 | } else { 2015 | max = +max || 0; 2016 | } 2017 | if (floating || min % 1 || max % 1) { 2018 | var rand = nativeRandom(); 2019 | return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max); 2020 | } 2021 | return baseRandom(min, max); 2022 | } 2023 | 2024 | /*--------------------------------------------------------------------------*/ 2025 | 2026 | lodash.assign = assign; 2027 | lodash.bind = bind; 2028 | lodash.createCallback = createCallback; 2029 | lodash.filter = filter; 2030 | lodash.forEach = forEach; 2031 | lodash.forIn = forIn; 2032 | lodash.forOwn = forOwn; 2033 | lodash.keys = keys; 2034 | lodash.map = map; 2035 | lodash.property = property; 2036 | 2037 | // add aliases 2038 | lodash.collect = map; 2039 | lodash.each = forEach; 2040 | lodash.extend = assign; 2041 | lodash.select = filter; 2042 | 2043 | /*--------------------------------------------------------------------------*/ 2044 | 2045 | // add functions that return unwrapped values when chaining 2046 | lodash.clone = clone; 2047 | lodash.identity = identity; 2048 | lodash.isArguments = isArguments; 2049 | lodash.isArray = isArray; 2050 | lodash.isFunction = isFunction; 2051 | lodash.isObject = isObject; 2052 | lodash.isString = isString; 2053 | lodash.noop = noop; 2054 | lodash.random = random; 2055 | lodash.reduce = reduce; 2056 | lodash.some = some; 2057 | 2058 | lodash.any = some; 2059 | lodash.foldl = reduce; 2060 | lodash.inject = reduce; 2061 | 2062 | /*--------------------------------------------------------------------------*/ 2063 | 2064 | /** 2065 | * The semantic version number. 2066 | * 2067 | * @static 2068 | * @memberOf _ 2069 | * @type string 2070 | */ 2071 | lodash.VERSION = '2.4.1'; 2072 | 2073 | /*--------------------------------------------------------------------------*/ 2074 | 2075 | if (freeExports && freeModule) { 2076 | 2077 | freeExports._ = lodash; 2078 | } 2079 | 2080 | }.call(this)); 2081 | -------------------------------------------------------------------------------- /lodash_custom/lodash.custom.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE 4 | * Build: `lodash exports="commonjs" include="assign,clone,filter,each,map,random,reduce,some"` 5 | */ 6 | ;(function(){function n(n){return typeof n.toString!="function"&&typeof(n+"")=="string"}function t(n){n.length=0,S.lengthe?0:e);++rk;k++)r+="n='"+e.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",e.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+e.g+"}";r+="}"}return(e.b||bt.nonEnumArgs)&&(r+="}"),r+=e.c+";return E",n("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",t+r+"}")(c,$,Y,ut,A,g,mt,v,q.f,Z,H,vt,M,nt,tt) 15 | }function s(n){return typeof n=="function"&&et.test(n)}function g(n){return n&&typeof n=="object"&&typeof n.length=="number"&&tt.call(n)==B||false}function y(n){return typeof n=="function"}function h(n){return!(!n||!H[typeof n])}function v(n){return typeof n=="string"||n&&typeof n=="object"&&tt.call(n)==M||false}function b(n,t,e){var o=[];if(t=r.createCallback(t,e,3),mt(n)){e=-1;for(var u=n.length;++earguments.length;if(t=r.createCallback(t,o,4),mt(n)){var a=-1,c=n.length;for(u&&(e=n[++a]);++a3&&typeof a[c-2]=='function'){var e=d(a[--c-1],a[c--],2)}else if(c>2&&typeof a[c-1]=='function'){e=a[--c]}"),g:"E[n]=e?e(E[n],t[n]):t[n]"}),Ot=p(U,wt,{j:false}),St=p(U,wt); 23 | y(/x/)&&(y=function(n){return typeof n=="function"&&"[object Function]"==tt.call(n)}),r.assign=xt,r.bind=w,r.createCallback=function(n,t,e){var r=typeof n;if(null==n||"function"==r)return c(n,t,e);if("object"!=r)return O(n);var o=Et(n),u=o[0],a=n[u];return 1!=o.length||a!==a||h(a)?function(t){for(var e=o.length,r=false;e--&&(r=l(t[o[e]],n[o[e]],null,true)););return r}:function(n){return n=n[u],a===n&&(0!==a||1/a==1/n)}},r.filter=b,r.forEach=d,r.forIn=Ot,r.forOwn=St,r.keys=Et,r.map=m,r.property=O,r.collect=m,r.each=d,r.extend=xt,r.select=b,r.clone=function(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=t,t=false),u(n,t,typeof e=="function"&&c(e,r,1)) 24 | },r.identity=_,r.isArguments=g,r.isArray=mt,r.isFunction=y,r.isObject=h,r.isString=v,r.noop=x,r.random=function(n,t,e){var r=null==n,o=null==t;return null==e&&(typeof n=="boolean"&&o?(e=n,n=1):o||typeof t!="boolean"||(e=t,o=true)),r&&o&&(t=1),n=+n||0,o?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=yt(),gt(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):n+rt(yt()*(t-n+1))},r.reduce=j,r.some=E,r.any=E,r.foldl=j,r.inject=j,r.VERSION="2.4.1",W&&Q&&(W._=r)}).call(this); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terra", 3 | "version": "1.5.2-beta", 4 | "description": "A JavaScript library for simple biological simulations and cellular automata.", 5 | "main": "dist/terra.min.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "terrarium", 11 | "simulator", 12 | "cellular", 13 | "automata" 14 | ], 15 | "author": "rileyjshaw ", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "gulp-rename": "~1.2.0", 19 | "gulp-concat": "~2.2.0", 20 | "gulp-ruby-sass": "~0.5.0", 21 | "gulp-uglify": "~0.3.1", 22 | "gulp-strip-debug": "~0.3.0", 23 | "gulp": "~3.8.5", 24 | "gulp-autoprefixer": "0.0.8", 25 | "gulp-jshint": "~1.6.4", 26 | "gulp-minify-css": "~0.3.6", 27 | "gulp-mocha": "~0.4.1", 28 | "minimist": "~0.2.0", 29 | "gulp-load-plugins": "~0.5.3", 30 | "vinyl-source-stream": "~0.1.1", 31 | "browserify": "~5.10.0", 32 | "gulp-streamify": "0.0.5", 33 | "gulp-gh-pages": "~0.3.3", 34 | "gulp-webserver": "~0.6.0" 35 | }, 36 | "dependencies": { 37 | "lodash": "~2.4.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | // Big TODO... --------------------------------------------------------------------------------