text: /**
   * Modules
   *
   * Copyright (c) 2013 Filatov Dmitry (dfilatov@yandex-team.ru)
   * Dual licensed under the MIT and GPL licenses:
   * http://www.opensource.org/licenses/mit-license.php
   * http://www.gnu.org/licenses/gpl.html
   *
   * @version 0.1.2
   */
  
  (function(global) {
  
  var undef,
  
      DECL_STATES = {
          NOT_RESOLVED : 'NOT_RESOLVED',
          IN_RESOLVING : 'IN_RESOLVING',
          RESOLVED     : 'RESOLVED'
      },
  
      /**
       * Creates a new instance of modular system
       * @returns {Object}
       */
      create = function() {
          var curOptions = {
                  trackCircularDependencies : true,
                  allowMultipleDeclarations : true
              },
  
              modulesStorage = {},
              waitForNextTick = false,
              pendingRequires = [],
  
              /**
               * Defines module
               * @param {String} name
               * @param {String[]} [deps]
               * @param {Function} declFn
               */
              define = function(name, deps, declFn) {
                  if(!declFn) {
                      declFn = deps;
                      deps = [];
                  }
  
                  var module = modulesStorage[name];
                  if(!module) {
                      module = modulesStorage[name] = {
                          name : name,
                          decl : undef
                      };
                  }
  
                  module.decl = {
                      name       : name,
                      prev       : module.decl,
                      fn         : declFn,
                      state      : DECL_STATES.NOT_RESOLVED,
                      deps       : deps,
                      dependents : [],
                      exports    : undef
                  };
              },
  
              /**
               * Requires modules
               * @param {String|String[]} modules
               * @param {Function} cb
               * @param {Function} [errorCb]
               */
              require = function(modules, cb, errorCb) {
                  if(typeof modules === 'string') {
                      modules = [modules];
                  }
  
                  if(!waitForNextTick) {
                      waitForNextTick = true;
                      nextTick(onNextTick);
                  }
  
                  pendingRequires.push({
                      deps : modules,
                      cb   : function(exports, error) {
                          error?
                              (errorCb || onError)(error) :
                              cb.apply(global, exports);
                      }
                  });
              },
  
              /**
               * Returns state of module
               * @param {String} name
               * @returns {String} state, possible values are NOT_DEFINED, NOT_RESOLVED, IN_RESOLVING, RESOLVED
               */
              getState = function(name) {
                  var module = modulesStorage[name];
                  return module?
                      DECL_STATES[module.decl.state] :
                      'NOT_DEFINED';
              },
  
              /**
               * Returns whether the module is defined
               * @param {String} name
               * @returns {Boolean}
               */
              isDefined = function(name) {
                  return !!modulesStorage[name];
              },
  
              /**
               * Sets options
               * @param {Object} options
               */
              setOptions = function(options) {
                  for(var name in options) {
                      if(options.hasOwnProperty(name)) {
                          curOptions[name] = options[name];
                      }
                  }
              },
  
              getStat = function() {
                  var res = {},
                      module;
  
                  for(var name in modulesStorage) {
                      if(modulesStorage.hasOwnProperty(name)) {
                          module = modulesStorage[name];
                          (res[module.decl.state] || (res[module.decl.state] = [])).push(name);
                      }
                  }
  
                  return res;
              },
  
              onNextTick = function() {
                  waitForNextTick = false;
                  applyRequires();
              },
  
              applyRequires = function() {
                  var requiresToProcess = pendingRequires,
                      i = 0, require;
  
                  pendingRequires = [];
  
                  while(require = requiresToProcess[i++]) {
                      requireDeps(null, require.deps, [], require.cb);
                  }
              },
  
              requireDeps = function(fromDecl, deps, path, cb) {
                  var unresolvedDepsCnt = deps.length;
                  if(!unresolvedDepsCnt) {
                      cb([]);
                  }
  
                  var decls = [],
                      onDeclResolved = function(_, error) {
                          if(error) {
                              cb(null, error);
                              return;
                          }
  
                          if(!--unresolvedDepsCnt) {
                              var exports = [],
                                  i = 0, decl;
                              while(decl = decls[i++]) {
                                  exports.push(decl.exports);
                              }
                              cb(exports);
                          }
                      },
                      i = 0, len = unresolvedDepsCnt,
                      dep, decl;
  
                  while(i < len) {
                      dep = deps[i++];
                      if(typeof dep === 'string') {
                          if(!modulesStorage[dep]) {
                              cb(null, buildModuleNotFoundError(dep, fromDecl));
                              return;
                          }
  
                          decl = modulesStorage[dep].decl;
                      }
                      else {
                          decl = dep;
                      }
  
                      decls.push(decl);
  
                      startDeclResolving(decl, path, onDeclResolved);
                  }
              },
  
              startDeclResolving = function(decl, path, cb) {
                  if(decl.state === DECL_STATES.RESOLVED) {
                      cb(decl.exports);
                      return;
                  }
                  else if(decl.state === DECL_STATES.IN_RESOLVING) {
                      curOptions.trackCircularDependencies && isDependenceCircular(decl, path)?
                          cb(null, buildCircularDependenceError(decl, path)) :
                          decl.dependents.push(cb);
                      return;
                  }
  
                  decl.dependents.push(cb);
  
                  if(decl.prev && !curOptions.allowMultipleDeclarations) {
                      provideError(decl, buildMultipleDeclarationError(decl));
                      return;
                  }
  
                  curOptions.trackCircularDependencies && (path = path.slice()).push(decl);
  
                  var isProvided = false,
                      deps = decl.prev? decl.deps.concat([decl.prev]) : decl.deps;
  
                  decl.state = DECL_STATES.IN_RESOLVING;
                  requireDeps(
                      decl,
                      deps,
                      path,
                      function(depDeclsExports, error) {
                          if(error) {
                              provideError(decl, error);
                              return;
                          }
  
                          depDeclsExports.unshift(function(exports, error) {
                              if(isProvided) {
                                  cb(null, buildDeclAreadyProvidedError(decl));
                                  return;
                              }
  
                              isProvided = true;
                              error?
                                  provideError(decl, error) :
                                  provideDecl(decl, exports);
                          });
  
                          decl.fn.apply(
                              {
                                  name   : decl.name,
                                  deps   : decl.deps,
                                  global : global
                              },
                              depDeclsExports);
                      });
              },
  
              provideDecl = function(decl, exports) {
                  decl.exports = exports;
                  decl.state = DECL_STATES.RESOLVED;
  
                  var i = 0, dependent;
                  while(dependent = decl.dependents[i++]) {
                      dependent(exports);
                  }
  
                  decl.dependents = undef;
              },
  
              provideError = function(decl, error) {
                  decl.state = DECL_STATES.NOT_RESOLVED;
  
                  var i = 0, dependent;
                  while(dependent = decl.dependents[i++]) {
                      dependent(null, error);
                  }
  
                  decl.dependents = [];
              };
  
          return {
              create     : create,
              define     : define,
              require    : require,
              getState   : getState,
              isDefined  : isDefined,
              setOptions : setOptions,
              getStat    : getStat
          };
      },
  
      onError = function(e) {
          nextTick(function() {
              throw e;
          });
      },
  
      buildModuleNotFoundError = function(name, decl) {
          return Error(decl?
              'Module "' + decl.name + '": can\'t resolve dependence "' + name + '"' :
              'Required module "' + name + '" can\'t be resolved');
      },
  
      buildCircularDependenceError = function(decl, path) {
          var strPath = [],
              i = 0, pathDecl;
          while(pathDecl = path[i++]) {
              strPath.push(pathDecl.name);
          }
          strPath.push(decl.name);
  
          return Error('Circular dependence has been detected: "' + strPath.join(' -> ') + '"');
      },
  
      buildDeclAreadyProvidedError = function(decl) {
          return Error('Declaration of module "' + decl.name + '" has already been provided');
      },
  
      buildMultipleDeclarationError = function(decl) {
          return Error('Multiple declarations of module "' + decl.name + '" have been detected');
      },
  
      isDependenceCircular = function(decl, path) {
          var i = 0, pathDecl;
          while(pathDecl = path[i++]) {
              if(decl === pathDecl) {
                  return true;
              }
          }
          return false;
      },
  
      nextTick = (function() {
          var fns = [],
              enqueueFn = function(fn) {
                  return fns.push(fn) === 1;
              },
              callFns = function() {
                  var fnsToCall = fns, i = 0, len = fns.length;
                  fns = [];
                  while(i < len) {
                      fnsToCall[i++]();
                  }
              };
  
          if(typeof process === 'object' && process.nextTick) { // nodejs
              return function(fn) {
                  enqueueFn(fn) && process.nextTick(callFns);
              };
          }
  
          if(global.setImmediate) { // ie10
              return function(fn) {
                  enqueueFn(fn) && global.setImmediate(callFns);
              };
          }
  
          if(global.postMessage && !global.opera) { // modern browsers
              var isPostMessageAsync = true;
              if(global.attachEvent) {
                  var checkAsync = function() {
                          isPostMessageAsync = false;
                      };
                  global.attachEvent('onmessage', checkAsync);
                  global.postMessage('__checkAsync', '*');
                  global.detachEvent('onmessage', checkAsync);
              }
  
              if(isPostMessageAsync) {
                  var msg = '__modules' + (+new Date()),
                      onMessage = function(e) {
                          if(e.data === msg) {
                              e.stopPropagation && e.stopPropagation();
                              callFns();
                          }
                      };
  
                  global.addEventListener?
                      global.addEventListener('message', onMessage, true) :
                      global.attachEvent('onmessage', onMessage);
  
                  return function(fn) {
                      enqueueFn(fn) && global.postMessage(msg, '*');
                  };
              }
          }
  
          var doc = global.document;
          if('onreadystatechange' in doc.createElement('script')) { // ie6-ie8
              var head = doc.getElementsByTagName('head')[0],
                  createScript = function() {
                      var script = doc.createElement('script');
                      script.onreadystatechange = function() {
                          script.parentNode.removeChild(script);
                          script = script.onreadystatechange = null;
                          callFns();
                      };
                      head.appendChild(script);
                  };
  
              return function(fn) {
                  enqueueFn(fn) && createScript();
              };
          }
  
          return function(fn) { // old browsers
              enqueueFn(fn) && setTimeout(callFns, 0);
          };
      })();
  
  if(typeof exports === 'object') {
      module.exports = create();
  }
  else {
      global.modules = create();
  }
  
  })(typeof window !== 'undefined' ? window : global);
  