text: /**
   * @author mrdoob / http://mrdoob.com/
   * @author alteredq / http://alteredqualia.com/
   */
  
  THREE.JSONLoader = function ( showStatus ) {
  
  	THREE.Loader.call( this, showStatus );
  
  };
  
  THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype );
  
  THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) {
  
  	var scope = this;
  
  	texturePath = texturePath ? texturePath : this.extractUrlBase( url );
  
  	this.onLoadStart();
  	this.loadAjaxJSON( this, url, callback, texturePath );
  
  };
  
  THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) {
  
  	var xhr = new XMLHttpRequest();
  
  	var length = 0;
  
  	xhr.onreadystatechange = function () {
  
  		if ( xhr.readyState === xhr.DONE ) {
  
  			if ( xhr.status === 200 || xhr.status === 0 ) {
  
  				if ( xhr.responseText ) {
  
  					var json = JSON.parse( xhr.responseText );
  					context.createModel( json, callback, texturePath );
  
  				} else {
  
  					console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there is empty" );
  
  				}
  
  				// in context of more complex asset initialization
  				// do not block on single failed file
  				// maybe should go even one more level up
  
  				context.onLoadComplete();
  
  			} else {
  
  				console.error( "THREE.JSONLoader: Couldn't load [" + url + "] [" + xhr.status + "]" );
  
  			}
  
  		} else if ( xhr.readyState === xhr.LOADING ) {
  
  			if ( callbackProgress ) {
  
  				if ( length === 0 ) {
  
  					length = xhr.getResponseHeader( "Content-Length" );
  
  				}
  
  				callbackProgress( { total: length, loaded: xhr.responseText.length } );
  
  			}
  
  		} else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) {
  
  			length = xhr.getResponseHeader( "Content-Length" );
  
  		}
  
  	};
  
  	xhr.open( "GET", url, true );
  	xhr.send( null );
  
  };
  
  THREE.JSONLoader.prototype.createModel = function ( json, callback, texturePath ) {
  
  	var scope = this,
  	geometry = new THREE.Geometry(),
  	scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
  
  	this.initMaterials( geometry, json.materials, texturePath );
  
  	parseModel( scale );
  
  	parseSkin();
  	parseMorphing( scale );
  
  	geometry.computeCentroids();
  	geometry.computeFaceNormals();
  
  	if ( this.hasNormals( geometry ) ) geometry.computeTangents();
  
  
  	function parseModel( scale ) {
  
  		function isBitSet( value, position ) {
  
  			return value & ( 1 << position );
  
  		}
  
  		var i, j, fi,
  
  		offset, zLength, nVertices,
  
  		colorIndex, normalIndex, uvIndex, materialIndex,
  
  		type,
  		isQuad,
  		hasMaterial,
  		hasFaceUv, hasFaceVertexUv,
  		hasFaceNormal, hasFaceVertexNormal,
  		hasFaceColor, hasFaceVertexColor,
  
  		vertex, face, color, normal,
  
  		uvLayer, uvs, u, v,
  
  		faces = json.faces,
  		vertices = json.vertices,
  		normals = json.normals,
  		colors = json.colors,
  
  		nUvLayers = 0;
  
  		// disregard empty arrays
  
  		for ( i = 0; i < json.uvs.length; i++ ) {
  
  			if ( json.uvs[ i ].length ) nUvLayers ++;
  
  		}
  
  		for ( i = 0; i < nUvLayers; i++ ) {
  
  			geometry.faceUvs[ i ] = [];
  			geometry.faceVertexUvs[ i ] = [];
  
  		}
  
  		offset = 0;
  		zLength = vertices.length;
  
  		while ( offset < zLength ) {
  
  			vertex = new THREE.Vector3();
  
  			vertex.x = vertices[ offset ++ ] * scale;
  			vertex.y = vertices[ offset ++ ] * scale;
  			vertex.z = vertices[ offset ++ ] * scale;
  
  			geometry.vertices.push( vertex );
  
  		}
  
  		offset = 0;
  		zLength = faces.length;
  
  		while ( offset < zLength ) {
  
  			type = faces[ offset ++ ];
  
  
  			isQuad          	= isBitSet( type, 0 );
  			hasMaterial         = isBitSet( type, 1 );
  			hasFaceUv           = isBitSet( type, 2 );
  			hasFaceVertexUv     = isBitSet( type, 3 );
  			hasFaceNormal       = isBitSet( type, 4 );
  			hasFaceVertexNormal = isBitSet( type, 5 );
  			hasFaceColor	    = isBitSet( type, 6 );
  			hasFaceVertexColor  = isBitSet( type, 7 );
  
  			//console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
  
  			if ( isQuad ) {
  
  				face = new THREE.Face4();
  
  				face.a = faces[ offset ++ ];
  				face.b = faces[ offset ++ ];
  				face.c = faces[ offset ++ ];
  				face.d = faces[ offset ++ ];
  
  				nVertices = 4;
  
  			} else {
  
  				face = new THREE.Face3();
  
  				face.a = faces[ offset ++ ];
  				face.b = faces[ offset ++ ];
  				face.c = faces[ offset ++ ];
  
  				nVertices = 3;
  
  			}
  
  			if ( hasMaterial ) {
  
  				materialIndex = faces[ offset ++ ];
  				face.materialIndex = materialIndex;
  
  			}
  
  			// to get face <=> uv index correspondence
  
  			fi = geometry.faces.length;
  
  			if ( hasFaceUv ) {
  
  				for ( i = 0; i < nUvLayers; i++ ) {
  
  					uvLayer = json.uvs[ i ];
  
  					uvIndex = faces[ offset ++ ];
  
  					u = uvLayer[ uvIndex * 2 ];
  					v = uvLayer[ uvIndex * 2 + 1 ];
  
  					geometry.faceUvs[ i ][ fi ] = new THREE.UV( u, v );
  
  				}
  
  			}
  
  			if ( hasFaceVertexUv ) {
  
  				for ( i = 0; i < nUvLayers; i++ ) {
  
  					uvLayer = json.uvs[ i ];
  
  					uvs = [];
  
  					for ( j = 0; j < nVertices; j ++ ) {
  
  						uvIndex = faces[ offset ++ ];
  
  						u = uvLayer[ uvIndex * 2 ];
  						v = uvLayer[ uvIndex * 2 + 1 ];
  
  						uvs[ j ] = new THREE.UV( u, v );
  
  					}
  
  					geometry.faceVertexUvs[ i ][ fi ] = uvs;
  
  				}
  
  			}
  
  			if ( hasFaceNormal ) {
  
  				normalIndex = faces[ offset ++ ] * 3;
  
  				normal = new THREE.Vector3();
  
  				normal.x = normals[ normalIndex ++ ];
  				normal.y = normals[ normalIndex ++ ];
  				normal.z = normals[ normalIndex ];
  
  				face.normal = normal;
  
  			}
  
  			if ( hasFaceVertexNormal ) {
  
  				for ( i = 0; i < nVertices; i++ ) {
  
  					normalIndex = faces[ offset ++ ] * 3;
  
  					normal = new THREE.Vector3();
  
  					normal.x = normals[ normalIndex ++ ];
  					normal.y = normals[ normalIndex ++ ];
  					normal.z = normals[ normalIndex ];
  
  					face.vertexNormals.push( normal );
  
  				}
  
  			}
  
  
  			if ( hasFaceColor ) {
  
  				colorIndex = faces[ offset ++ ];
  
  				color = new THREE.Color( colors[ colorIndex ] );
  				face.color = color;
  
  			}
  
  
  			if ( hasFaceVertexColor ) {
  
  				for ( i = 0; i < nVertices; i++ ) {
  
  					colorIndex = faces[ offset ++ ];
  
  					color = new THREE.Color( colors[ colorIndex ] );
  					face.vertexColors.push( color );
  
  				}
  
  			}
  
  			geometry.faces.push( face );
  
  		}
  
  	};
  
  	function parseSkin() {
  
  		var i, l, x, y, z, w, a, b, c, d;
  
  		if ( json.skinWeights ) {
  
  			for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) {
  
  				x = json.skinWeights[ i     ];
  				y = json.skinWeights[ i + 1 ];
  				z = 0;
  				w = 0;
  
  				geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
  
  			}
  
  		}
  
  		if ( json.skinIndices ) {
  
  			for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) {
  
  				a = json.skinIndices[ i     ];
  				b = json.skinIndices[ i + 1 ];
  				c = 0;
  				d = 0;
  
  				geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
  
  			}
  
  		}
  
  		geometry.bones = json.bones;
  		geometry.animation = json.animation;
  
  	};
  
  	function parseMorphing( scale ) {
  
  		if ( json.morphTargets !== undefined ) {
  
  			var i, l, v, vl, dstVertices, srcVertices;
  
  			for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
  
  				geometry.morphTargets[ i ] = {};
  				geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
  				geometry.morphTargets[ i ].vertices = [];
  
  				dstVertices = geometry.morphTargets[ i ].vertices;
  				srcVertices = json.morphTargets [ i ].vertices;
  
  				for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
  
  					var vertex = new THREE.Vector3();
  					vertex.x = srcVertices[ v ] * scale;
  					vertex.y = srcVertices[ v + 1 ] * scale;
  					vertex.z = srcVertices[ v + 2 ] * scale;
  
  					dstVertices.push( vertex );
  
  				}
  
  			}
  
  		}
  
  		if ( json.morphColors !== undefined ) {
  
  			var i, l, c, cl, dstColors, srcColors, color;
  
  			for ( i = 0, l = json.morphColors.length; i < l; i++ ) {
  
  				geometry.morphColors[ i ] = {};
  				geometry.morphColors[ i ].name = json.morphColors[ i ].name;
  				geometry.morphColors[ i ].colors = [];
  
  				dstColors = geometry.morphColors[ i ].colors;
  				srcColors = json.morphColors [ i ].colors;
  
  				for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) {
  
  					color = new THREE.Color( 0xffaa00 );
  					color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );
  					dstColors.push( color );
  
  				}
  
  			}
  
  		}
  
  	};
  
  	callback( geometry );
  
  };
  