jplusplus/the-accountant

View on GitHub
src/components/stack/stack.service.js

Summary

Maintainability
B
5 hrs
Test Coverage
import _ from 'lodash';
export default StackService;

/** @ngInject */
function StackService(Slice, I18n) {
  // Symbols declarion for private attributes and methods
  const _meta = Symbol('meta');
  const _slice = Symbol('slice');
  const _parent = Symbol('parent');
  const _clusters = Symbol('clusters');

  class Stack extends I18n {
    constructor(meta = [], parent = null) {
      super(meta);
      this[_meta] = angular.copy(meta);
      // Start before 0 (ie no slice)
      this[_slice] = -1;
      // Save the parent
      this[_parent] = parent;
      // Ensure those method arround bound to the current instance
      ['sliceFilter', 'isStartingSlice', 'isFirstSlice', 'isLastSlice', 'clusterFilter', 'continue'].forEach(m => {
        this[m] = this[m].bind(this);
      });
    }
    isStartingSlice() {
      return this.slice === -1;
    }
    isFirstSlice() {
      return this.slice === 0;
    }
    isLastSlice() {
      return this.slice === this.slices.length - 1;
    }
    continue() {
      this.slice = this.slice + 1;
    }
    finalSlice() {
      this.slice = this.slices.length - 1;
    }
    clusterFilter(cluster, index) {
      // Clusters before this one
      const previous = this.clusters.slice(0, index);
      // Count seen slices within those clusters
      const seen = _.chain(previous).map('slices.length').sum().value();
      // Display this cluster
      return seen <= this.slice;
    }
    sliceFilter(slice) {
      return slice.index <= this.slice;
    }
    isTyping() {
      return Boolean(this.next);
    }
    findGame(obj = this) {
      if (obj.meta && obj.meta.characters) {
        return obj;
      } else if (!(obj instanceof Stack) && obj.game) {
        return obj.game;
      } else if (obj.parent) {
        return this.findGame(obj.parent);
      }
    }
    // Express reading time of the current slice in milliseconds
    get readingTime() {
      return this.current ? this.current.readingTime : 0;
    }
    set slice(val) {
      this[_slice] = Math.max(-1, Math.min(this.slices.length - 1, val));
    }
    get slice() {
      return this[_slice];
    }
    get next() {
      return this.slices[this.slice + 1] || null;
    }
    get previous() {
      return this.slices[this.slice - 1] || null;
    }
    get current() {
      return this.slices[this.slice] || null;
    }
    get slices() {
      return this.memoize('slices', () => {
        // Create slices
        return _.compact(this[_meta]).map(slice => new Slice(slice, this));
      });
    }
    get parent() {
      return this[_parent];
    }
    get game() {
      return this.findGame();
    }
    // We regroup slices by "cluster" meaning they are regroupped by
    // successive slices of the same character.
    get clusters() {
      if (this[_clusters]) {
        return this[_clusters];
      }
      // Initialize clusters list
      this[_clusters] = [];
      // Iterates over ever slices
      this.slices.forEach(slice => {
        // Get last cluster
        const last = _.last(this[_clusters]);
        // Is there any cluster and is it the same character?
        if (last && slice.canClusterizeWith(last)) {
          // Add this slice
          last.slices.push(slice);
        // No cluster or a different character
        } else {
          // Create a cluster using the current slice
          this[_clusters].push({
            character: slice.character,
            type: slice.type,
            slices: [slice]
          });
        }
      });
      return this[_clusters];
    }
  }
  return Stack;
}