Source: Rekord.js


/**
 * Creates a Rekord object given a set of options. A Rekord object is also the
 * constructor for creating instances of the Rekord object defined.
 *
 * @namespace
 * @param {Object} options
 *        The options of
 */
function Rekord(options)
{
  var promise = Rekord.get( options.name );

  if ( promise.isComplete() )
  {
    return promise.results[0];
  }

  Rekord.trigger( Rekord.Events.Options, [options] );

  var database = new Database( options );

  var model = new Function('return function ' + database.className + '(props, remoteData) { this.$init( props, remoteData ) }')(); // jshint ignore:line
  model.prototype = new Model( database );

  database.Model = model;
  model.Database = database;

  Rekord.classes[ database.name ] = model;

  Rekord.trigger( Rekord.Events.Plugins, [model, database, options] );

  if ( Rekord.autoload )
  {
    database.loadBegin(function onLoadFinish(success)
    {
      if ( success )
      {
        database.loadFinish();
      }
    });
  }
  else
  {
    Rekord.unloaded.push( database );
  }

  Rekord.get( database.name ).resolve( model );
  Rekord.get( database.className ).resolve( model );

  Rekord.debug( Rekord.Debugs.CREATION, database, options );

  return model;
}

Rekord.classes = {};

Rekord.autoload = false;

Rekord.unloaded = [];

Rekord.loadPromise = null;

Rekord.load = function(callback, context)
{
  var promise = Rekord.loadPromise = Rekord.loadPromise || new Promise( null, false );
  var loading = Rekord.unloaded.slice();
  var loaded = [];
  var loadedSuccess = [];

  promise.success( callback, context || this );

  Rekord.unloaded.length = 0;

  function onLoadFinish(success, db)
  {
    loadedSuccess.push( success );
    loaded.push( db );

    if ( loaded.length === loading.length )
    {
      for (var k = 0; k < loaded.length; k++)
      {
        var db = loaded[ k ];
        var success = loadedSuccess[ k ];

        if ( success )
        {
          db.loadFinish();
        }
      }

      promise.reset().resolve();
    }
  }

  for (var i = 0; i < loading.length; i++)
  {
    loading[ i ].loadBegin( onLoadFinish );
  }

  return promise;
};

Rekord.promises = {};

Rekord.get = function(name)
{
  var existing = Rekord.promises[ name ];

  if ( !existing )
  {
    existing = Rekord.promises[ name ] = new Promise( null, false );
  }

  return existing;
};

/**
 * A value which identifies a model instance. This can be the key of the model,
 * an array of values (if the model has composite keys), an object which at
 * least contains fields which identify the model, an instance of a model, the
 * reference to a Rekord instance, or a function.
 *
 * If a plain object is given and it shares the same key as an existing model -
 * the other fields on the object will be applied to the existing instance. If
 * a plain object is given and it's key doesn't map to an existing model - a new
 * one is created.
 *
 * If a reference to a Rekord instance is given - a new model instance is created
 * with default values.
 *
 * If a function is given - it's invoked and the returning value is used as the
 * value to identify the model instance.
 *
 * @typedef {String|Number|String[]|Number[]|Object|Rekord|Rekord.Model|Function} modelInput
 */

 /**
  * A key to a model instance.
  *
  * @typedef {String|Number} modelKey
  */

addEventful( Rekord );

Rekord.Events =
{
  Initialized:  'initialized',
  Plugins:      'plugins',
  Options:      'options',
  Online:       'online',
  Offline:      'offline'
};

var Cascade =
{
  None:       0,
  Local:      1,
  Rest:       2,
  NoLive:     3,
  Live:       4,
  NoRest:     5,
  Remote:     6,
  All:        7
};

function canCascade(cascade, type)
{
  return !isNumber( cascade ) || (cascade & type) === type;
}

var Cache =
{
  None:       'none',
  Pending:    'pending',
  All:        'all'
};

var Store =
{
  None:   0,
  Model:  1,
  Key:    2,
  Keys:   3
};

var Save =
{
  None:   0,
  Model:  4,
  Key:    5,
  Keys:   6
};

var Load =
{
  None:   0,
  All:    1,
  Lazy:   2,
  Both:   3
};

var RestStatus =
{
  Conflict:   {409: true},
  NotFound:   {404: true, 410: true},
  Offline:    {0: true}
};