cardkit.js

const deepExtend = require('deep-extend');

/**
 * @name CardKit
 * @class Core CardKit class used for managing a single card instance
 */
class CardKit {

  /**
   * Constructor takes in the configuration and stores it for later user
   *
   * @param {object} configuration - The configuration object to initialise the CardKit image with.
   * @param {object} options - The additional options for use
   */
  constructor (configuration, options = false) {
    if (!configuration) {
      throw new Error('A configuration object was not provided')
    }

    if (!this._isValidConfiguration(configuration)) {
      throw new Error('Invalid configuration object provided')
    }

    // Store the configuration
    this.configuration = configuration;

    // Configure the options
    this._configureOptions(options);

    // Setup an empty array of renderers
    this.renderers = [];
  }

  /**
   * Configures the supplied options on this instance of CardKit
   *
   * @param {object} options - The options to configure
   */
  _configureOptions (options) {
    if (options) {
      if (options.templates) {
        if (!this._isValidTemplatesConfiguration(options.templates)) {
          throw new Error('Invalid templates configuration object provided');
        }

        this.templates = options.templates
      } else {
        this.templates = null;
      }

      if (options.themes) {
        if (!this._isValidThemesConfiguration(options.themes)) {
          throw new Error('Invalid themes configuration object provided');
        }

        this.themes = options.themes
      } else {
        this.themes = null;
      }

      if (options.layouts) {
        if (!this._isValidLayoutsConfiguration(options.layouts)) {
          throw new Error('Invalid layouts configuration object provided');
        }

        this.layouts = options.layouts
      } else {
        this.layouts = null;
      }
    }
  }

  /**
   * Validates the provided configuration object
   *
   * @param {object} configuration - The configuration object to validate
   *
   * @return {boolean} Is the configuration object valid
   */
  _isValidConfiguration (configuration) {
    return (typeof configuration === 'object') && // Should be an object
           (typeof configuration.card !== 'undefined') && // Should have a card property
           (typeof configuration.card === 'object') && // Card property should be an object
           (typeof configuration.card.height !== 'undefined') && // Should have a height
           (typeof configuration.card.width !== 'undefined'); // Should have a width
  }

  /**
   * Validates the provided templates configuration object
   *
   * @param {object} configuration - The templates configuration object to validate
   *
   * @return {boolean} Is the templates configuration object valid
   */
  _isValidTemplatesConfiguration (templates) {
    return (typeof templates === 'object'); // Should be an object
  }

  /**
   * Validates the provided themes configuration object
   *
   * @param {object} configuration - The themes configuration object to validate
   *
   * @return {boolean} Is the themes configuration object valid
   */
  _isValidThemesConfiguration (themes) {
    return (typeof themes === 'object'); // Should be an object
  }

  /**
   * Validates the provided layouts configuration object
   *
   * @param {object} configuration - The layouts configuration object to validate
   *
   * @return {boolean} Is the layouts configuration object valid
   */
  _isValidLayoutsConfiguration (layouts) {
    return (typeof layouts === 'object'); // Should be an object
  }

  /**
   * Validates the supplied renderer
   *
   * @param {object} renderer - The renderer to validate
   *
   * @return {boolean} If the renderer is valid
   */
  _isValidRenderer (renderer) {
    return (renderer.cardkit === this);
  }

  /**
   * Compute the configuration
   *
   * @param {object} options - Any options (e.g. a specific theme and / or layout) to use when computing the configuration
   *
   * @return {object} The computed configuration
   */
  computeConfiguration (options = null) {
    // Get the base configuration
    let configuration = Object.assign({}, this.configuration);

    // If we got options supplied
    if (options) {
      if (options.template && typeof this.templates[options.template] !== 'undefined') {
        // Get the template based on the name and merge it onto the base configuration
        configuration = deepExtend(configuration, this.templates[options.template]);
      }

      if (options.theme && typeof this.themes[options.theme] !== 'undefined') {
        // Get the theme based on the name and merge it onto the base configuration
        configuration = deepExtend(configuration, this.themes[options.theme]);
      }

      if (options.layout && typeof this.layouts[options.layout] !== 'undefined') {
        // Get the layout based on the name and merge it onto the base configuration
        configuration = deepExtend(configuration, this.layouts[options.layout]);
      }
    }

    // Return the computed configuration
    return configuration;
  }

  /**
   * Updates the configuration, and optionally rerenders the image (if previously rendered)
   *
   * @param {object} configuration - The configuration object to update to
   * @param {object} options - Any options to supply (templates, themes, layouts)
   * @param {boolean} rerender - Whether or not to attempt to rerender the image
   */
  updateConfiguration (configuration, options = { layouts: null, templates: null, themes: null }, rerender = true) {
    this.configuration = configuration;

    this._configureOptions(options);

    if (rerender) {
      const renderers = this.getRenderers();

      renderers.forEach((renderer) => {
        switch (renderer.constructor.name) {
          case 'CardKitDOM':
            renderer.rerender();
            break;
        }
      });
    }
  }

  /**
   * Get the renderers
   *
   * @return {array} The configured renderers
   */
  getRenderers () {
    return this.renderers;
  }

  /**
   * Add a renderer
   *
   * @param {object} renderer - A renderer to add
   */
  addRenderer (renderer) {
    if (!this._isValidRenderer(renderer)) {
      throw new Error('Invalid renderer object provided')
    }

    this.renderers.push(renderer);
  }
}

// Export it
module.exports = CardKit

// Add it to the window object if we have one
if (typeof window !== 'undefined') {
  window.CardKit = CardKit
}