diff options
author | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-21 02:30:48 +0000 |
---|---|---|
committer | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-21 02:30:48 +0000 |
commit | 87b76ac6325a46826d67dca20c351154ad3d94a8 (patch) | |
tree | 0337d73c98fff54ec89946bd9f786979daa58552 /o3d/samples | |
parent | 130e036f019117d3341c2a15d06e5004e32c9501 (diff) | |
download | chromium_src-87b76ac6325a46826d67dca20c351154ad3d94a8.zip chromium_src-87b76ac6325a46826d67dca20c351154ad3d94a8.tar.gz chromium_src-87b76ac6325a46826d67dca20c351154ad3d94a8.tar.bz2 |
Incremental progress toward archive loading in o3d-webgl. Implemented
ArchiveRequest, which expects to be pointed to the top-level scene.json,
and which emulates the behavior of the O3D plugin by downloading the
referenced resources and creating RawData objects for each. Pulled in
public domain JSON parser from http://www.json.org/ . Implemented portions
of TextureCUBE. Fixed some unimplemented functionality and a few bugs
related to creation of parameters. At this point a larger refactoring of
o3d-webgl is needed to make the deserialization work properly, so this code
is being checked in as a checkpoint.
Also fixed small bug in the cg_to_glsl converter script.
BUG=none
TEST=none
TBR=petersont,amarinichev
Review URL: http://codereview.chromium.org/1745002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45143 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples')
-rw-r--r-- | o3d/samples/o3d-webgl-samples/helloworld.html | 174 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/archive_request.js | 268 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/base.js | 14 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/file_request.js | 4 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/pack.js | 41 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param_object.js | 13 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/raw_data.js | 2 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/texture.js | 14 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/transform.js | 22 | ||||
-rw-r--r-- | o3d/samples/third_party/json/json2-min.js | 29 | ||||
-rw-r--r-- | o3d/samples/third_party/json/json2.js | 482 |
11 files changed, 1045 insertions, 18 deletions
diff --git a/o3d/samples/o3d-webgl-samples/helloworld.html b/o3d/samples/o3d-webgl-samples/helloworld.html new file mode 100644 index 0000000..654e975 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/helloworld.html @@ -0,0 +1,174 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- +O3D Tutorial A1 + +In this tutorial, we load and display a scene in O3D. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Tutorial A1: Loading a scene +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> + +<!-- Our javascript code --> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.camera'); +o3djs.require('o3djs.pack'); +o3djs.require('o3djs.scene'); + +// Events +// init() once the page has finished loading. +window.onload = init; +// unload() when leaving the page. +window.onunload = unload; + +// global variables +var g_o3d; +var g_math; +var g_client; +var g_viewInfo; +var g_pack; +var g_finished = false; // for selenium testing + +/** + * Loads a scene into the transform graph. + * @param {!o3d.Pack} pack Pack to load scene into. + * @param {string} fileName filename of the scene. + * @param {!o3d.Transform} parent parent node in the transform graph to + * which to load the scene into. + */ +function loadScene(pack, fileName, parent) { + // Get our full path to the scene + var scenePath = o3djs.util.getCurrentURI() + fileName; + + // Load the file given the full path, and call the callback function + // when its done loading. + o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback); + + /** + * Our callback is called once the scene has been loaded into memory + * from the web or locally. + * @param {!o3d.Pack} pack The pack that was passed in above. + * @param {!o3d.Transform} parent The parent that was passed in above. + * @param {*} exception null if loading succeeded. + */ + function callback(pack, parent, exception) { + if (exception) { + alert('Could not load: ' + fileName + '\n' + exception); + return; + } + // Get a CameraInfo (an object with a view and projection matrix) + // using our javascript library function + var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras( + parent, + g_client.width, + g_client.height); + + // Copy the view and projection to the draw context. + g_viewInfo.drawContext.view = cameraInfo.view; + g_viewInfo.drawContext.projection = cameraInfo.projection; + + // Generate draw elements and setup material draw lists. + o3djs.pack.preparePack(pack, g_viewInfo); + + g_finished = true; // for selenium testing. + } +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Remove any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + if (g_client) { + g_client.cleanup(); + } +} + +/** + * Initializes O3D and loads the scene into the transform graph. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + g_client = o3dElement.client; + + // Creates a pack to manage our resources/assets + g_pack = g_client.createPack(); + + // Create the render graph for a view. + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Creates a transform to put our data on. + var myDataRoot = g_pack.createObject('Transform'); + + // Connects our root to the client root. + myDataRoot.parent = g_client.root; + + // Load the scene into the transform graph as a child myDataRoot + loadScene(g_pack, '../assets/teapot/scene.json', myDataRoot); +} +</script> +</head> +<body> +<h1>Loading a scene.</h1> +This tutorial shows how we load and display a scene in O3D. +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/o3d-webgl/archive_request.js b/o3d/samples/o3d-webgl/archive_request.js new file mode 100644 index 0000000..e35282a --- /dev/null +++ b/o3d/samples/o3d-webgl/archive_request.js @@ -0,0 +1,268 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// TODO(kbr): figure out how we can reuse the o3djs.io package from +// within here. +// o3djs.require('o3djs.io'); + +// TODO(kbr): factor this out into e.g. o3djs.json and require +// o3djs.json here. +if(!this.JSON){this.JSON={};} +(function(){function f(n){return n<10?'0'+n:n;} +if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ +f(this.getUTCMonth()+1)+'-'+ +f(this.getUTCDate())+'T'+ +f(this.getUTCHours())+':'+ +f(this.getUTCMinutes())+':'+ +f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} +var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} +function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} +if(typeof rep==='function'){value=rep.call(holder,key,value);} +switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} +gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';} +v=partial.length===0?'[]':gap?'[\n'+gap+ +partial.join(',\n'+gap)+'\n'+ +mind+']':'['+partial.join(',')+']';gap=mind;return v;} +if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}} +v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+ +mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}} +if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;} +rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');} +return str('',{'':value});};} +if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}} +return reviver.call(holder,key,value);} +text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+ +('0000'+a.charCodeAt(0).toString(16)).slice(-4);});} +if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;} +throw new SyntaxError('JSON.parse');};}}()); + + +/** + An ArchiveRequest object is used to carry out an asynchronous request for a + compressed archive (containing multiple files). + + Note: The archive must have as its first file a file named 'aaaaaaaa.o3d' + who's contents is 'o3d'. This is to prevent O3D being used to open + archive files that were not meant for it. + + \code + var request = pack.createArchiveRequest(); + request.open("GET", url); + + request.onfileavailable = myFileAvailableCallback; + request.onreadystatechange = myReadyStateChangeCallback; + request.send(); + + function myFileAvailableCallback(rawData) { + dump("uri: " + rawData.uri + "\n"); + dump("content: " + rawData.stringValue + "\n"); + + // You can pass a RawData to various creation functions. Note: rawData + // is only valid until you remove the request. + // Examples: + if (rawData.uri == 'mytexture.jpg') + pack.createTexture2d(rawData, makeMips); + if (rawData.uri == 'myvertices.bin') + vertexBuffer.set(rawData); + if (rawData.uri == 'myAudio.mp3') + audioSystem.createSound(rawData); + } + + function myReadyStateChangeCallback() { + if (request.done) { + if (request.success) { + // there were no errors trying to read the archive + } else { + dump(request.error); + } + } + } + + // When you are done with the RawDatas loaded by the request, remove + // the request from the pack to free them. + pack.removeObject(request); +*/ + +o3d.ArchiveRequest = function() { + o3d.ObjectBase.call(this); + this.method_ = null; +}; +o3d.inherit('ArchiveRequest', 'ObjectBase'); + +/** + * The URI this request is for. + * @type {string} + */ +o3d.ArchiveRequest.prototype.uri = ''; + +/** + * Set up several of the request fields. + * @param {string} method "GET" is the only supported method at this time + * @param {string} uri the location of the file to fetch + * @param {boolean} async true is the only legal value at this time. + */ +o3d.ArchiveRequest.prototype.open = + function(method, uri) { + this.uri = uri; + + // Compute the parent directory of this URI. + var parentURI = uri; + var lastSlash = uri.lastIndexOf('/'); + if (lastSlash != -1) { + parentURI = parentURI.substring(0, lastSlash + 1); + } + + this.parentURI_ = parentURI; +}; + +/** + * Send the request. + * Unlike XMLHttpRequest the onreadystatechange callback will be called no + * matter what, with success or failure. + */ +o3d.ArchiveRequest.prototype.send = function() { + var that = this; + this.done = false; + this.success = true; + this.error = null; + var callback = function(sourceJSON, exc) { + // Don't send down the original scene.json because 'eval' is used + // elsewhere to reconstitute it, which is risky. + var filteredJSON = JSON.stringify(JSON.parse(sourceJSON)); + + if (that.onfileavailable) { + var rawData = new o3d.RawData(); + rawData.uri = 'scene.json'; + rawData.stringValue = filteredJSON; + that.onfileavailable(rawData); + } + + // In o3d-webgl, the "archive" is really just the top-level + // scene.json. We run a regexp on it to find URIs for certain + // well-known file types (.fx, .png, .jpg) and issue file requests + // for them. + var uriRegex = /\"([^\"]*\.(fx|png|jpg))\"/g; + var matchArray; + var uris = []; + while ((matchArray = uriRegex.exec(sourceJSON)) != null) { + uris.push(matchArray[1]); + } + + that.pendingRequests_ = uris.length; + + // Issue requests for each of these URIs. + for (var ii = 0; ii < uris.length; ++ii) { + if (that.stringEndsWith_(uris[ii], ".fx")) { + var func = function(uri) { + var completion = function(value, exc) { + var rawData = null; + if (exc == null) { + rawData = new o3d.RawData(); + rawData.uri = uri; + rawData.stringValue = value; + } + that.decrementPendingRequests_(rawData, exc); + }; + o3djs.io.loadTextFile(that.relativeToAbsoluteURI_(uri), + completion); + }; + func(uris[ii]); + } else if (that.stringEndsWith_(uris[ii], ".png") || + that.stringEndsWith_(uris[ii], ".jpg")) { + var func = function(uri) { + var image = new Image(); + image.onload = function() { + var rawData = new o3d.RawData(); + rawData.uri = uri; + rawData.image_ = image; + that.decrementPendingRequests_(rawData, exc); + }; + image.onerror = function() { + that.decrementPendingRequests_(null, exc); + } + image.src = that.relativeToAbsoluteURI_(uri); + }; + func(uris[ii]); + } + } + }; + + o3djs.io.loadTextFile(this.uri, callback); +}; + +/** + * A callback to call whenever the ready state of the request changes. + * @type {function(): void} + */ +o3d.ArchiveRequest.prototype.onreadystatechange = null; + +/** + * A callback to call when each file comes in. + * @type {function(!o3d.RawData): void} + */ +o3d.ArchiveRequest.prototype.onfileavailable = null; + +/** + * Converts a local URI to an absolute URI. + * @private + */ +o3d.ArchiveRequest.prototype.relativeToAbsoluteURI_ = function(relativeURI) { + return this.parentURI_ + relativeURI; +}; + +/** + * Indicates whether one string ends with another. + * @private + */ +o3d.ArchiveRequest.prototype.stringEndsWith_ = function(string, suffix) { + return string.substring(string.length - suffix.length) == suffix; +}; + +/** + * Decrements the number of pending requests. + * @private + */ +o3d.ArchiveRequest.prototype.decrementPendingRequests_ = + function(rawData, opt_exc) { + this.success = this.success && rawData && (!opt_exc); + if (opt_exc != null) { + this.error = "" + opt_exc; + } + if (rawData && this.onfileavailable) { + this.onfileavailable(rawData); + } + if (--this.pendingRequests_ == 0) { + this.done = true; + if (this.onreadystatechange) { + this.onreadystatechange(); + } + } +}; diff --git a/o3d/samples/o3d-webgl/base.js b/o3d/samples/o3d-webgl/base.js index 9962de5..d5e01cc 100644 --- a/o3d/samples/o3d-webgl/base.js +++ b/o3d/samples/o3d-webgl/base.js @@ -121,6 +121,19 @@ o3d.writeScriptTag_ = function(src) { }; /** + * Filters any "o3d." prefix from the given type name. + * @param {string} type_name The type name to filter. + * @return {string} Filtered type name. + * @private + */ +o3d.filterTypeName_ = function(type_name) { + if (type_name.length >= 4 && type_name.substr(0, 4) == 'o3d.') { + type_name = type_name.substr(4); + } + return type_name; +}; + +/** * Includes the file indicated by the rule by adding a script tag. * @param {string} rule Rule to include, in the form o3d.package.part. */ @@ -220,5 +233,6 @@ o3d.include('primitive'); o3d.include('shape'); o3d.include('effect'); o3d.include('material'); +o3d.include('archive_request'); diff --git a/o3d/samples/o3d-webgl/file_request.js b/o3d/samples/o3d-webgl/file_request.js index 8c0d44c..164e184 100644 --- a/o3d/samples/o3d-webgl/file_request.js +++ b/o3d/samples/o3d-webgl/file_request.js @@ -187,6 +187,10 @@ o3d.FileRequest.prototype.imageLoaded_ = function() { o3d.FileRequest.prototype.open = function(method, uri, async) { this.uri = uri; + // TODO(petersont): I think there is a race condition here -- calling + // code expects that it can still set up the onreadystatechange callback + // between open() and send(), but if open() actually initiates the XHR + // then the caller may miss the crucial completion callback! if (this.isImageUrl_(uri)) { this.image_ = new Image(); var that = this; diff --git a/o3d/samples/o3d-webgl/pack.js b/o3d/samples/o3d-webgl/pack.js index 7ff3577..a9f79bb 100644 --- a/o3d/samples/o3d-webgl/pack.js +++ b/o3d/samples/o3d-webgl/pack.js @@ -171,7 +171,7 @@ o3d.Pack.prototype.removeObject = */ o3d.Pack.prototype.createObject = function(type_name) { - var foo = o3d.global.o3d[type_name]; + var foo = o3d.global.o3d[o3d.filterTypeName_(type_name)]; if (typeof foo != 'function') { throw 'cannot find type in o3d namespace: ' + type_name } @@ -232,18 +232,37 @@ o3d.Pack.prototype.createTexture2D = * Note: If enable_render_surfaces is true, then the dimensions must be a * power of two. * - * @param {number} edge_length The edge of the texture area in texels + * @param {number} edgeLength The edge of the texture area in texels * (max = 2048) * @param {o3d.Texture.Format} format The memory format of each texel. * @param {number} levels The number of mipmap levels. Use zero to create * the compelete mipmap chain. - * @param {boolean} enable_render_surfaces If true, the texture object + * @param {boolean} enableRenderSurfaces If true, the texture object * will expose RenderSurface objects through GetRenderSurface(...). * @return {!o3d.TextureCUBE} The TextureCUBE object. */ o3d.Pack.prototype.createTextureCUBE = - function(edge_length, format, levels, enable_render_surfaces) { - o3d.notImplemented(); + function(edgeLength, format, levels, enableRenderSurfaces) { + var texture = this.createObject('TextureCUBE'); + texture.edgeLength = edgeLength; + texture.texture_ = this.gl.createTexture(); + + this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, texture.texture_); + for (var ii = 0; ii < 6; ++ii) { + this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + ii, + 0, this.gl.RGBA, edgeLength, edgeLength, 0, + this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); + } + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, + this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, + this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, + this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, + this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); + + return texture; }; @@ -280,9 +299,7 @@ o3d.Pack.prototype.createDepthStencilSurface = */ o3d.Pack.prototype.getObjects = function(name, class_type_name) { - if (class_type_name.substr(0, 4) == 'o3d.') { - class_type_name = class_type_name.substr(4); - } + class_type_name = o3d.filterTypeName_(class_type_name); var found = []; @@ -361,6 +378,14 @@ o3d.Pack.prototype.createFileRequest = return this.createObject('FileRequest'); }; +/** + * Creates an ArchiveRequest so we can stream in assets from an archive. + * @return {!o3d.ArchiveRequest} an ArchiveRequest + */ +o3d.Pack.prototype.createArchiveRequest = + function() { + return this.createObject('ArchiveRequest'); +}; /** * Create Bitmaps from RawData. diff --git a/o3d/samples/o3d-webgl/param_object.js b/o3d/samples/o3d-webgl/param_object.js index 7749df2..f59f674 100644 --- a/o3d/samples/o3d-webgl/param_object.js +++ b/o3d/samples/o3d-webgl/param_object.js @@ -102,13 +102,14 @@ o3d.ParamObject.prototype.createParam = function(param_name, param_type_name) { if (this.params_[param_name]) return null; + param_type_name = o3d.filterTypeName_(param_type_name); if (!o3d.global.o3d[param_type_name]) throw ('Invalid param type name: ' + param_type_name); var param = new o3d.global.o3d[param_type_name]; param.gl = this.gl; param.owner_ = this; this.params_[param_name] = param; - return this.params_[param_name]; + return this.filterResult_(this.params_[param_name]); }; @@ -120,7 +121,7 @@ o3d.ParamObject.prototype.createParam = */ o3d.ParamObject.prototype.getParam = function(param_name) { - return this.params_[param_name]; + return this.filterResult_(this.params_[param_name]); }; @@ -169,4 +170,10 @@ o3d.ParamObject.prototype.copyParams = o3d.notImplemented(); }; - +/** + * Filters results, turning 'undefined' into 'null'. + * @private + */ +o3d.ParamObject.prototype.filterResult_= function(result) { + return (result ? result : null); +}; diff --git a/o3d/samples/o3d-webgl/raw_data.js b/o3d/samples/o3d-webgl/raw_data.js index 59a441c..80a3489f 100644 --- a/o3d/samples/o3d-webgl/raw_data.js +++ b/o3d/samples/o3d-webgl/raw_data.js @@ -55,7 +55,7 @@ o3d.inherit('RawData', 'NamedObject'); * and the uri must end in .json, .txt, .xml, .ini or .csv * @type {string} */ -o3d.RawData.prototype.string_value = ''; +o3d.RawData.prototype.stringValue = ''; /** diff --git a/o3d/samples/o3d-webgl/texture.js b/o3d/samples/o3d-webgl/texture.js index 90bda54..ce78349 100644 --- a/o3d/samples/o3d-webgl/texture.js +++ b/o3d/samples/o3d-webgl/texture.js @@ -376,7 +376,7 @@ o3d.TextureCUBE.FACE_NEGATIVE_Z = 5; * The length of each edge of the cube, in texels. * @type {number} */ -o3d.TextureCUBE.prototype.edge_length = 0; +o3d.TextureCUBE.prototype.edgeLength = 0; /** @@ -469,7 +469,17 @@ o3d.TextureCUBE.prototype.getRect = */ o3d.TextureCUBE.prototype.setFromBitmap = function(face, bitmap) { - o3d.notImplemented(); + this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.texture_); + this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, + 0, // Level. + bitmap.canvas_, + false); // Do not flip cube maps' faces. + // TODO(petersont): figure out when we should call generateMipmaps. + // TODO(petersont): move generateMips to Texture2D / TextureCUBE + // classes because the target needs to differ. + // if (bitmap.defer_mipmaps_to_texture_) { + // this.generateMips(); + // } }; diff --git a/o3d/samples/o3d-webgl/transform.js b/o3d/samples/o3d-webgl/transform.js index 68b02f3..6ed797b 100644 --- a/o3d/samples/o3d-webgl/transform.js +++ b/o3d/samples/o3d-webgl/transform.js @@ -135,10 +135,25 @@ o3d.Transform.prototype.addChild = function(child) { */ o3d.Transform.prototype.getTransformsInTree = function() { - + var result = []; + o3d.Transform.getTransformInTreeRecursive_(this, result); + return result; }; +/** + * Recursive helper function for getTransformInTree. + * @private + */ +o3d.Transform.getTransformInTreeRecursive_ = + function(treeRoot, children) { + children.push(treeRoot); + var childrenArray = treeRoot.children; + for (var ii = 0; ii < childrenArray.length; ++ii) { + o3d.Transform.getTransformInTreeRecursive_(childrenArray[ii], children); + } +}; + /** * Searches for transforms that match the given name in the hierarchy under and @@ -155,10 +170,9 @@ o3d.Transform.prototype.getTransformsInTree = */ o3d.Transform.prototype.getTransformsByNameInTree = function(name) { - + o3d.notImplemented(); }; - /** * Evaluates and returns the current world matrix. * @@ -166,7 +180,7 @@ o3d.Transform.prototype.getTransformsByNameInTree = */ o3d.Transform.prototype.getUpdatedWorldMatrix = function() { - + o3d.notImplemented(); }; diff --git a/o3d/samples/third_party/json/json2-min.js b/o3d/samples/third_party/json/json2-min.js new file mode 100644 index 0000000..a2c7769 --- /dev/null +++ b/o3d/samples/third_party/json/json2-min.js @@ -0,0 +1,29 @@ + +if(!this.JSON){this.JSON={};} +(function(){function f(n){return n<10?'0'+n:n;} +if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ +f(this.getUTCMonth()+1)+'-'+ +f(this.getUTCDate())+'T'+ +f(this.getUTCHours())+':'+ +f(this.getUTCMinutes())+':'+ +f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} +var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} +function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} +if(typeof rep==='function'){value=rep.call(holder,key,value);} +switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} +gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';} +v=partial.length===0?'[]':gap?'[\n'+gap+ +partial.join(',\n'+gap)+'\n'+ +mind+']':'['+partial.join(',')+']';gap=mind;return v;} +if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}} +v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+ +mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}} +if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;} +rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');} +return str('',{'':value});};} +if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}} +return reviver.call(holder,key,value);} +text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+ +('0000'+a.charCodeAt(0).toString(16)).slice(-4);});} +if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;} +throw new SyntaxError('JSON.parse');};}}());
\ No newline at end of file diff --git a/o3d/samples/third_party/json/json2.js b/o3d/samples/third_party/json/json2.js new file mode 100644 index 0000000..a1a3b17 --- /dev/null +++ b/o3d/samples/third_party/json/json2.js @@ -0,0 +1,482 @@ +/* + http://www.JSON.org/json2.js + 2010-03-20 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/. +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); |