Source: client/core.js

/**
 * @file Volt Core Library - plug-in registration
 *
 * @license
 * (c) 2017 NS BASIC Corporation. All rights reserved.
 */

window.$volt = (function () {
  'use strict';

  var plugins = {}, pluginNames = [];

  // static object to manage internal state
  var state = {
    // clear all state items except for appId
    clear: function () {
      var key;

      for (key in this) {
        if (typeof this[key] !== 'function' && key !== 'appId') {
          delete this[key];
        }
      }

      this.save();
    },

    // loads the state (given) an appId, from localStorage
    load: function (appId) {
      var key, state = window.localStorage.getItem('$volt_' + appId);

      try {
        state = JSON.parse(state);
      } catch (e) {
        state = null;
      }

      if (state && typeof state === 'object') {
        for (key in state) {
          if (state.hasOwnProperty(key)) {
            this[key] = state[key];
          }
        }
      }

      this.appId = appId;
    },

    // saves the state to localStorage - assumes appId is set in the state
    save: function () {
      window.localStorage.setItem('$volt_' + this.appId, JSON.stringify(this));
    }
  };

  /**
   * Volt Core Library
   *
   * Provides plugin registratoin, and basic authentication and user management.
   *
   * @namespace
   * @alias $volt
   */
  var ns = {  // jscs:ignore requireVarDeclFirst
    /**
     * Register a Volt plug-in
     *
     * More details are available in the plug-in spec - this is
     * private for now as it is only needed by plug-in authors
     *
     * @private
     *
     * @param {string} name - the name of the mount point for the plug-in in the `$volt` namespace - can contain periods which represent hierarchy
     * @param {function} init - the callback function with the params (core, state) that inits the plug-in
     */
    register: function (name, init) {
      if (!plugins.hasOwnProperty(name)) {
        plugins[name] = init;
        pluginNames.push(name);
      } else {
        throw new Error('Plug-in "' + name + '" already registered.');
      }
    },

    /**
     * Init Volt and registered plug-ins
     *
     * @param {string} appId - the appId to init Volt with
     * @param {string} [domain] - the API domain
     */
    init: function (appId, domain) {
      var i, name, init, mount, part;

      state.load(appId);
      state.domain = domain;

      // make sure parent namespaces are created prior to children
      pluginNames = pluginNames.sort();

      for (i = 0; i < pluginNames.length; i++) {
        name = pluginNames[i].split('.');
        init = plugins[pluginNames[i]];

        // walk the dots down
        mount = ns;
        part = name.shift();
        while (name.length) {
          mount[part] = mount[part] || {};  // create missing namespaces
          mount = mount[part];
          part = name.shift();
        }

        mount[part] = init(ns, state);
      }
    },

    /**
     * Helper method to allow callback methods to also use promises if
     * support exists in the browser
     *
     * @private
     *
     * @example
     * function callbackOrPromise(callback) {
     *   callback = methodAsPromised(callback);
     *
     *   // do stuff
     *
     *   return callback.promise;
     * }
     *
     * @param {voltCallback} [callback] the callback parameter from
     * the calling function, or undefined if it wasn't passed
     *
     * @returns {voltCallback} either the original or newly created
     * callback - if the callback was created, it has an additional
     * property `promise` which is a reference to the promise to
     * return
     */
    methodAsPromised: function (callback) {
      var promise;
      var resolve, reject;

      if (!callback) {
        if (!window.Promise) {
          throw new Error("This browser doesn't support promises.");
        }

        promise = new window.Promise(function (res, rej) {
          resolve = res;
          reject = rej;
        });

        callback = function (error, data) {
          if (error) {
            error.data = data;
            reject(error);
          } else {
            resolve(data);
          }
        };

        callback.promise = promise;
      }

      return callback;
    }
  };

  return ns;
})();