Entity System
Here is most of the code to run the above
For more information, check out my article explaining it. Use the arrow keys to move
				
//ES.js - takes a Canvas 2D context
function ES(ctx){

	//a useful extension of the Array class
	Array.prototype.contains = function(obj) {
		var i = this.length;
		while (i--) {
			if (this[i] === obj) {
				return true;
			}
		}
		return false;
	}
	
	/*
	'components' is array of arrays. One component is an array, containing an entity ID, and another array of data
	Components (array):
		(1) component array:
			(1) entityID (int)
			(2) data (dictionary): //aka associative array, for example, a shape
				//data["type"] = "square" //we really need to avoid strings...
				data["rgba"] = "rgba(0, 0, 200, 0.5)"
				data["x"] = 0
				data["y"] = 29
				startx, starty, endx, endy ...
				
		(2) component (array):
			(1) entityID (int)
			(2) data (dictionary):
				...
	*/
	
	/*
	'revComponents' is an array of arrays
	*/

	/*
	'subsystems' are realized as a collection of functions (objects). They must be initialized.
	All subsystems must have a function named 'call' which accepts ???? components?
	*/
	var subsystems = new Array();

	//this holds the components and entities. allows modifications of them, etc.
	function CEStorage (){
		/*
		entities holds all entities, and an array of what components they have
		*/
		this.entities = new Array(); //given an _entity_, get the components associated with it, and data
		
		this.components = new Array(); //given the _component_, get all entities associated with it, and data
		
		CEStorage.prototype.addEntity = function(){
			//find new entity
			var entityNum = this.entities.length;
			console.log("creating new entity: " + entityNum);
			//add it
			var entityArr = new Array();

			this.entities[entityNum] = entityArr;
			return entityNum; 
		}
		
		CEStorage.prototype.registerComponent = function(strName){
			//componentLookup[strName
			this.components[strName] = new Array();
			//we don't deal with ids and whatnot to begin with... later.
		}
		
		CEStorage.prototype.addComponent = function(entity, component, data){
			if(this.entities[entity]){ //entity exists and is 'initiated' 
				if(this.components[component]){ //if component exists too
					var componentEntry = new Array();
					componentEntry[0] = entity;
					
					//also update the entities array
					if(!this.entities[entity].contains(component)){
						this.entities[entity].push(component);
					}
					
					if (data instanceof Array){
						componentEntry[1] = data;
						this.components[component].push(componentEntry);
					} else {
						console.log("err: data must be an array");
					}
				} else {
					console.log("err: component doesn't exist");
				}
			} else {
				console.log("err: entity doesn't exist");
			}
		}
		
		CEStorage.prototype.removeComponent = function(entity, component){
			var len = this.entities[entity].length;
			for(var i=0; i < len; i++) {
				if (this.entities[entity][i] === component) {
					delete this.entities[entity][i];
					return true;
				}
			}
			return false;
		}
		
		//returns components that a given entity has
		CEStorage.prototype.getComponents = function(entity){
			return this.entities[entity]; 
		}
		
		//returns all entities (and component data) of a given component
		CEStorage.prototype.getEntities = function(component){
			return this.components[component];
		}
	}
	
	//Bare-bones template of a subsystem. Follow the naming convention XxxxxSubsystem
	function GenericSubsystem(CEStorage){
		this.CE = CEStorage;
		//put initiation code here
		
		XxxxxSubsystem.prototype.activate = function(){
			//you must implement this method
			
			//this method runs once every game tick
		}
	}
	
	//all subsystems must implement the function 'activate'
	function RenderSubsystem(CEStorage, canvas){
		this.CE = CEStorage;
		this.c = canvas;
		
		RenderSubsystem.prototype.activate = function(){
			//find all relevant components
			var renderableComponents = this.CE.getEntities("renderable");
			
			//clear
			this.c.clearRect(0, 0, this.c.canvas.width, this.c.canvas.height);
				
			for(var i=0; i<renderableComponents.length; i++){
				var entity = renderableComponents[i][0];
				var data = renderableComponents[i][1];
				
				//given its data, render it on the graphics context
				this.c.fillStyle = data["rgba"]; //EX: "rgba(0, 0, 200, 0.5)";
				this.c.fillRect (data["x"] + data["startx"], data["y"] + data["starty"], data["endx"],  data["endy"]);
			}
		}
	}
	
	function InputSubsystem(CEStorage){
		this.CE = CEStorage;
		keys = new Array(); 
		
		//register event listeners
		onkeydown=function(e){
			var e=window.event || e;
			//console.log("keydown: " + e.keyCode);
			keys[e.keyCode] = true;
			return false;	
		}
		
		onkeyup=function(e){
			var e=window.event || e;
			//console.log("keyup: " + e.keyCode);
			keys[e.keyCode] = false;
			return false;
		}
		
		InputSubsystem.prototype.activate = function(){
			//find all relevant components
			var input = this.CE.getEntities("input");
			
			for(var i=0; i<input.length; i++){
				var entity = input[i][0];
				var data = input[i][1];
				
				for(var index in data) {
					//console.log("data["+index+"] = " + data[index]);
					data[index] = keys[index];
				}
			}
		}
	}
	
	
	function SimpleMovementSubsystem(CEStorage){
		this.CE = CEStorage;
		
		SimpleMovementSubsystem.prototype.activate = function(){
			var input = this.CE.getEntities("input");
			
			for(var i=0; i<input.length; i++){
				var entity = input[i][0];
				var data = input[i][1];
				
				for(var j=0; j<data.length; j++){
					if(data[j]){
						var xOff = 0;
						var yOff = 0;
						switch(j){
							case 37:	//left
								xOff = -2;
								break;
							case 38:	//up
								yOff = -2;
								break;
							case 39:	//right
								xOff = 2;
								break;
							case 40:	//down
								yOff = 2;
								break;
						}
						this.move(entity, xOff, yOff);
					}
				}
			}
		}
		
		SimpleMovementSubsystem.prototype.move = function(entity, xOff, yOff){
			var renderable = this.CE.getEntities("renderable");
			//find entity, and change its x/y offset
			for(var i=0; i<renderable.length; i++){
				if(renderable[i][0] == entity){
					var data = renderable[i][1];
					data["x"] = data["x"] + xOff;
					data["y"] = data["y"] + yOff;
				}
			}
		}
	}
	
	//takes an initialized object. Kthx
	function registerSubsystem(subsystem){
		subsystems.push(subsystem);
	}
	
	function gameLoop(){
		//loop through each subsystem
		for(var i=0; i<subsystems.length; i++){
			subsystems[i].activate();
		}
	}
	
	
	
	//// TESTING CODE: (could be moved elsewhere, perhaps outside of the ES function?... why do we even have an ES function?)
	var CE = new CEStorage();
	
	//initialize and register all subsystems here (put in separate function?)
	registerSubsystem(new RenderSubsystem(CE, ctx));
	registerSubsystem(new InputSubsystem(CE));
	registerSubsystem(new SimpleMovementSubsystem(CE));
	
	//register some components
	CE.registerComponent("renderable");
	CE.registerComponent("input");
	CE.registerComponent("simpleMovement");
	CE.registerComponent("position");
	
	var t = CE.addEntity();
	var dataz = new Array();
	dataz["y"] = 10;
	dataz["x"] = 50;
	dataz["startx"] = 0;
	dataz["starty"] = 0;
	dataz["endx"] = 10;
	dataz["endy"] = 15;
	dataz["rgba"] = "rgba(100, 0, 100, 0.5)";
	CE.addComponent(t, "renderable", dataz);
	
	//add entity
	var tmpEntity = CE.addEntity();
	
	//adding input
	var inputs = new Array();
	inputs[37] = false; //left
	inputs[38] = false; //up
	inputs[39] = false; //right
	inputs[40] = false; //down 
	CE.addComponent(tmpEntity, "input", inputs);
	
	//add renderability
	var data = new Array();
	data["rgba"] = "rgba(0, 0, 200, 0.5)";
	data["x"] = 5;
	data["y"] = 10;
	data["startx"] = 0;
	data["starty"] = 0;
	data["endx"] = 10;
	data["endy"] = 15;
	CE.addComponent(tmpEntity, "renderable", data);
	CE.addComponent(tmpEntity, "simpleMovement", data);
	
	console.log("beginning gameLoop()");
	setInterval(gameLoop, 30); // 33~=30 FPS
	gameLoop();
	console.log("end");
	
}

// we also need to calculate FPS values differently for slow/fast browsers/computers. Run in a 'tight' loop?

// the 'delete' function leaves a hole in the entity array data structure. if 'input' and then 'renderable' were added, then input removed, then input added
// again (and renderable removed), then repeat the process, the array will have a very long list of [undefined, undefined ... ]. This is in the
// removeComponent() function

// it would be more efficient to maintain a link to each component/entity/data pair in the entities data structure.

// screw that, we need a better data structure.