/* * 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. */ /** * @fileoverview Base for all o3d sample utilties. * For more information about o3d see * http://code.google.com/p/o3d. * * * The main point of this module is to provide a central place to * have an init function to register an o3d namespace object because many other * modules need access to it. */ /** * A namespace for all the o3djs utility libraries. * @namespace */ var o3djs = o3djs || {}; /** * Define this because the Google internal JSCompiler needs goog.typedef below. */ var goog = goog || {}; /** * A macro for defining composite types. * * By assigning goog.typedef to a name, this tells Google internal JSCompiler * that this is not the name of a class, but rather it's the name of a composite * type. * * For example, * /** @type {Array|NodeList} / goog.ArrayLike = goog.typedef; * will tell JSCompiler to replace all appearances of goog.ArrayLike in type * definitions with the union of Array and NodeList. * * Does nothing in uncompiled code. */ goog.typedef = true; /** * Reference to the global context. In most cases this will be 'window'. */ o3djs.global = this; /** * Flag used to force a function to run in the browser when it is called * from V8. * @type {boolean} */ o3djs.BROWSER_ONLY = true; /** * Array of namespaces that have been provided. * @private * @type {!Array.} */ o3djs.provided_ = []; /** * Creates object stubs for a namespace. When present in a file, * o3djs.provide also indicates that the file defines the indicated * object. * @param {string} name name of the object that this file defines. */ o3djs.provide = function(name) { // Ensure that the same namespace isn't provided twice. if (o3djs.getObjectByName(name) && !o3djs.implicitNamespaces_[name]) { throw 'Namespace "' + name + '" already declared.'; } var namespace = name; while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { o3djs.implicitNamespaces_[namespace] = true; } o3djs.exportPath_(name); o3djs.provided_.push(name); }; /** * Namespaces implicitly defined by o3djs.provide. For example, * o3djs.provide('o3djs.events.Event') implicitly declares * that 'o3djs' and 'o3djs.events' must be namespaces. * * @type {Object} * @private */ o3djs.implicitNamespaces_ = {}; /** * Builds an object structure for the provided namespace path, * ensuring that names that already exist are not overwritten. For * example: * "a.b.c" -> a = {};a.b={};a.b.c={}; * Used by o3djs.provide and o3djs.exportSymbol. * @param {string} name name of the object that this file defines. * @param {Object} opt_object the object to expose at the end of the path. * @param {Object} opt_objectToExportTo The object to add the path to; default * is |o3djs.global|. * @private */ o3djs.exportPath_ = function(name, opt_object, opt_objectToExportTo) { var parts = name.split('.'); var cur = opt_objectToExportTo || o3djs.global; var part; // Internet Explorer exhibits strange behavior when throwing errors from // methods externed in this manner. See the testExportSymbolExceptions in // base_test.html for an example. if (!(parts[0] in cur) && cur.execScript) { cur.execScript('var ' + parts[0]); } // Parentheses added to eliminate strict JS warning in Firefox. while (parts.length && (part = parts.shift())) { if (!parts.length && o3djs.isDef(opt_object)) { // last part and we have an object; use it. cur[part] = opt_object; } else if (cur[part]) { cur = cur[part]; } else { cur = cur[part] = {}; } } }; /** * Returns an object based on its fully qualified external name. If you are * using a compilation pass that renames property names beware that using this * function will not find renamed properties. * * @param {string} name The fully qualified name. * @param {Object} opt_obj The object within which to look; default is * |o3djs.global|. * @return {Object} The object or, if not found, null. */ o3djs.getObjectByName = function(name, opt_obj) { var parts = name.split('.'); var cur = opt_obj || o3djs.global; for (var pp = 0; pp < parts.length; ++pp) { var part = parts[pp]; if (cur[part]) { cur = cur[part]; } else { return null; } } return cur; }; /** * Implements a system for the dynamic resolution of dependencies. * @param {string} rule Rule to include, in the form o3djs.package.part. */ o3djs.require = function(rule) { // TODO(gman): For some unknown reason, when we call // o3djs.util.getScriptTagText_ it calls // document.getElementsByTagName('script') and for some reason the scripts do // not always show up. Calling it here seems to fix that as long as we // actually ask for the length, at least in FF 3.5.1 It would be nice to // figure out why. var dummy = document.getElementsByTagName('script').length; // if the object already exists we do not need do do anything if (o3djs.getObjectByName(rule)) { return; } var path = o3djs.getPathFromRule_(rule); if (path) { o3djs.included_[path] = true; o3djs.writeScripts_(); } else { throw new Error('o3djs.require could not find: ' + rule); } }; /** * Path for included scripts. * @type {string} */ o3djs.basePath = ''; /** * Object used to keep track of urls that have already been added. This * record allows the prevention of circular dependencies. * @type {Object} * @private */ o3djs.included_ = {}; /** * This object is used to keep track of dependencies and other data that is * used for loading scripts. * @private * @type {Object} */ o3djs.dependencies_ = { visited: {}, // used when resolving dependencies to prevent us from // visiting the file twice. written: {} // used to keep track of script files we have written. }; /** * Tries to detect the base path of the o3djs-base.js script that * bootstraps the o3djs libraries. * @private */ o3djs.findBasePath_ = function() { var doc = o3djs.global.document; if (typeof doc == 'undefined') { return; } if (o3djs.global.BASE_PATH) { o3djs.basePath = o3djs.global.BASE_PATH; return; } else { // HACKHACK to hide compiler warnings :( o3djs.global.BASE_PATH = null; } var scripts = doc.getElementsByTagName('script'); for (var script, i = 0; script = scripts[i]; i++) { var src = script.src; var l = src.length; if (src.substr(l - 13) == 'o3djs/base.js') { o3djs.basePath = src.substr(0, l - 13); return; } } }; /** * Writes a script tag if, and only if, that script hasn't already been added * to the document. (Must be called at execution time.) * @param {string} src Script source. * @private */ o3djs.writeScriptTag_ = function(src) { var doc = o3djs.global.document; if (typeof doc != 'undefined' && !o3djs.dependencies_.written[src]) { o3djs.dependencies_.written[src] = true; doc.write('