Cobalt = window.Cobalt || {};
(function() {
  if (typeof Cobalt.require == 'function') return;

  Cobalt.require = require;
  Cobalt.requireAll = requireAll;
  Cobalt.requireCSS = requireCSS
  Cobalt.loadCSS = loadCSS;

  /*
   * Cobalt.require: dynamic JavaScript loader
   *
   * Synopsis:
   *
   *  Cobalt.require(objectName, filePath [, callback])
   *
   *    objectName  : string  : name of a global object to test for; if
   *                            it exists, the file won't be reloaded.
   *                            (null bypasses this test; always re-request).
   *    filePath    : string  : URL (relative or absolute) of the JavaScript
   *                            source file to load.
   *    callback    : string  : [optional] function to be invoked after
   *                            require has either loaded the source file
   *                            or verified its existence.
   */

  var requireCache = {};

  function fileLoaded(path,name) {
    for (var key in requireCache) {
      var obj = requireCache[key];
      if (obj.path == path) {
        if (obj.loaded) return obj;
        else return false;
      }
    }
    if (typeof requireCache[name] != 'undefined') {
      throw new Error('Reload of ' + name + ' from "' + path
                      + '"; originally loaded from "' + requireCache[name].path
                      + '"');
    }
    return false;
  }

  function extend(obj,source,secure) {
    if (secure) {
      for (var prop in obj) if (prop in source) obj[prop] = source[prop];
    } else {
      for (var prop in source) obj[prop] = source[prop];
    }
    return obj;
  }

  var nextID_value = (new Date).getTime();
  function insertInHead(elm,type,callback) {
    var head = document.getElementsByTagName('head'),
        typemap = { script: 'text/javascript', style:  'text/css' },
        idmap = { script: 'Dscript_', style:  'Dstyle_' };
    function nextID(root) {
      return root + (nextID_value++);
    }
    if (head.length > 0) {
      elm.type = typemap[type];
      elm.id = nextID(idmap[type]);
      elm.onload = elm.onreadystatechange = function() {
        if (typeof callback == 'function') callback.call(elm);
      }
      head[0].appendChild(elm);
    }
  }

  function require(objName,path,callback) {
    var obj,complete = false;

    if (objName !== null && (typeof window[objName] != 'undefined') || (obj = fileLoaded(path,objName)))
      callback(obj ? document.getElementById(obj.id) : null); // callback specific to this requirement
    else {
      var tag = document.createElement('script');
      if (tag) {
        tag.src = path;
        if (typeof callback != 'function') callback = function() {};
        insertInHead(tag,'script',function() {
          if (!complete && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
            complete = true;
            if (objName !== null) {
                requireCache[objName] = {id:tag.id,path:path,loaded:true,callback:callback};
            }
            callback();
          }
        });
      }
    }
  }


  /*
   * Cobalt.requireAll: dynamic multiple JavaScript loader
   *
   * Synopsis:
   *
   *  Cobalt.requireAll(filePathArray [, callback])
   *
   *    filePathArray : array  : URLs (relative or absolute) of the
   *                             JavaScript source files to load.
   *    callback      : string : [optional] function to be invoked after
   *                             requireAll has loaded *all* source files
   *
   * Note:
   *  Script load order is undefined (i.e., not predictable). If this 
   *  is a problem, you should chain calls to Cobalt.require(), e.g.:
   *
   *    Cobalt.require('a',scriptA_url,function() {
   *      Cobalt.require('b',scriptB_url,function() {
   *        Cobalt.require('c',scriptC_url,function() {
   *          alert('Scripts loaded in order: A, B, C');
   *        });
   *      });
   *    });
   */

  function requireAll(pathArray,callback) {
    var loadMarkers = {};
    function maybeCallCallback() {
      for (var p in loadMarkers) {
        if (!loadMarkers[p]) return;
      }
      callback();
    }
    if (typeof callback != 'function') callback = function() {};
    for (var i=pathArray.length; i--; ) {
      var path = pathArray[i];
      loadMarkers[path] = false;
      require(null,path,(function(path) {
          return function() {loadMarkers[path] = true; maybeCallCallback()};
      })(path));
    }
  }

  /*
   * Cobalt.requireCSS: dynamic stylesheet loader
   *
   * Synopsis:
   *
   *  Cobalt.requireCSS(filePath [, args])
   *
   *    filePath    : string  : URL (relative or absolute) of the stylesheet
   *                            source file to load.
   *    args        : object  : [optional] object with override stylesheet
   *                            properties
   */

  function requireCSS(path,args) {
    var tag = document.createElement('link'),
        options = {
          type:     'text/css'
         ,media:    'screen'
         ,rel:      'stylesheet'
         ,href:     path
       };

    if (tag) {
      extend(tag,extend(options,(args || {}),true),false);
      insertInHead(tag,'style');
    }
  }

  function loadCSS(styleSheetText) {
    var tag = document.createElement('style');
    if (tag) {
      tag.type = 'text/css';
      if (tag.styleSheet) { // IE
        tag.styleSheet.cssText = styleSheetText;
      } else {
        tag.appendChild(document.createTextNode(styleSheetText));
      }
      insertInHead(tag,'style');
    }
  }
})();

