<commit_before>'use strict';

var m = require('mithril');
var _ = require('underscore');
var classNames = require('classnames');
var Browser = require('../browser');

var GridComponent = {};

GridComponent.controller = function (game) {
  var ctrl = {
    // Initialize position of pending chip to the leftmost column
    pendingChipX: 0,
    pendingChipY: 0,
    // Booleans indicating when to transition the pending chip's movement in a
    // particular direction (for example, the pending chip should never
    // transition when resetting to its initial position after placing a chip)
    transitionPendingChipX: false,
    transitionPendingChipY: false,
    // Get the CSS translate string for the given coordinate map
    getTranslate: function (coords) {
      return 'translate(' + coords.x + 'px,' + coords.y + 'px)';
    },
    // Update the translation coordinates for the pending chip (this will take
    // effect on the next redraw)
    setPendingChipCoords: function (coords) {
      this.pendingChipX = coords.x;
      this.pendingChipY = coords.y;
    },
    // Retrieve the constant width of a single chip
    getChipWidth: function (grid) {
      var gridElem = document.getElementById('grid');
      return gridElem.offsetWidth / grid.columnCount;
    },
    // Get the index of the last visited column (the column where the cursor was
    // last at or where the last chip was dropped)
    getLastVisitedColumnIndex: function (grid, event) {
      var chipWidth = this.getChipWidth(grid);
      return Math.max(0, Math.floor((event.pageX - event.currentTarget.offsetLeft) / chipWidth));
    },
    // Move the pending chip to be aligned with the specified column
    movePendingChipToColumn: function (args) {
      // The last visited column is the grid column nearest to the cursor at
      // any given instant; keep track of the column's X position so the next
      // pending chip can instantaneously appear there
      this.lastVisitedColumnX = this.getChipWidth(args.game.grid) * args.column;
      this.setPendingChipCoords({
        x: this.lastVisitedColumnX,
        y: 0
      });
      this.transitionPendingChipX = true;
      this.transitionPendingChipY = false;
    },
    // Move the pending chip into alignment with the column nearest to the
    // user's cursor
    movePendingChipViaPointer: function (ctrl, game, event) {
      if (game.pendingChip && game.currentPlayer.type === 'human' && !ctrl.transitionPendingChipY) {
        var pointerColumnIndex = ctrl.getLastVisitedColumnIndex(game.grid, event);
        ctrl.movePendingChipToColumn({
          game: game,
          column: pointerColumnIndex
        });
      } else {
        m.redraw.strategy('none');
      }
    },
    // Get the coordinates of the chip slot element at the given column/row
    getSlotCoords: function (args) {
      var chipWidth = this.getChipWidth(args.grid);
      return {
        x: chipWidth * args.column,
        y: chipWidth * (args.grid.rowCount - args.row)
      };
    },
    // Place the pending chip into the specified column (or move the chip to
    // that column without placing it if the chip is not currently aligned with
    // the column)
    placePendingChip: function (args) {
      var rowIndex = args.game.grid.getNextAvailableSlot({
        column: args.column
      });
      // Do not allow user to place chip in column that is already full
      if (rowIndex === null) {
        m.redraw.strategy('none');
        return;
      }
      var slotCoords = this.getSlotCoords({
        grid: args.game.grid,
        column: args.column,
        row: rowIndex
      });
      // If pending chip is not currently aligned with chosen column
      if (this.pendingChipX !== slotCoords.x) {
        // First move pending chip into alignment with column
        this.movePendingChipToColumn({
          game: args.game,
          column: args.column
        });
        // Since AI players can't click to place a chip after the chip realigns
        // with the chosen column, place the chip automatically
        if (args.game.currentPlayer.type === 'AI') {
          var ctrl = this;
          game.emitter.once('pending-chip:transition-end', function () {
            args.game.currentPlayer.wait(function () {
              ctrl.placePendingChip(args);
            });
          });
        }
      } else {
        // Otherwise, chip is already aligned; drop chip into place on grid
        this.transitionPendingChipX = false;
        this.transitionPendingChipY = true;
        // Keep track of where chip was dropped
        this.lastVisitedColumnX = slotCoords.x;
        // Translate chip to the visual position on the grid corresponding to
        // the above column and row
        this.setPendingChipCoords(slotCoords);
        // Perform insertion on internal game grid once transition has ended
        this.finishPlacingPendingChip(args);
      }
      m.redraw();
    },
    // Place the pending chip into the column where the user clicked
    placePendingChipViaPointer: function (ctrl, game, event) {
      if (game.pendingChip && game.currentPlayer.type === 'human' && !ctrl.transitionPendingChipY) {
        ctrl.placePendingChip({
          game: game,
          column: ctrl.getLastVisitedColumnIndex(game.grid, event)
        });
      } else {
        m.redraw.strategy('none');
      }
    },
    // Actually insert the pending chip into the internal grid once the falling
    // transition has ended
    finishPlacingPendingChip: function (args) {
      var ctrl = this;
      game.emitter.once('pending-chip:transition-end', function finish() {
        args.game.placePendingChip({column: args.column});
        ctrl.transitionPendingChipX = false;
        ctrl.transitionPendingChipY = false;
        // Reset position of pending chip to the space directly above the last
        // visited column
        ctrl.setPendingChipCoords({
          x: ctrl.lastVisitedColumnX,
          y: 0
        });
        m.redraw();
      });
    },
    // Configure pending chip element when it's first created
    configurePendingChip: function (ctrl, game, pendingChipElem) {
      if (!ctrl.boundPendingChipTransitionEnd) {
        ctrl.boundPendingChipTransitionEnd = true;
        var eventName = Browser.normalizeEventName('transitionend');
        pendingChipElem.addEventListener(eventName, function () {
          game.emitter.emit('pending-chip:transition-end');
        });
      }
    }
  };
  // Place chip automatically when AI computes its next move on its turn
  game.emitter.on('ai-player:compute-next-move', function (chosenColumn) {
    ctrl.placePendingChip({
      game: game,
      column: chosenColumn
    });
  });
  return ctrl;
};
GridComponent.view = function (ctrl, game) {
  var grid = game.grid;
  return m('div#grid', {
    class: classNames({'has-winner': game.winner !== null}),
    onmousemove: _.partial(ctrl.movePendingChipViaPointer, ctrl, game),
    onclick: _.partial(ctrl.placePendingChipViaPointer, ctrl, game)
  }, [
    // The chip that is about to be placed on the grid
    game.pendingChip ?
      m('div', {
        class: classNames(
          'chip',
          'pending',
          game.pendingChip.player.color,
           {'transition-x': ctrl.transitionPendingChipX},
           {'transition-y': ctrl.transitionPendingChipY}
        ),
        style: Browser.normalizeStyles({
          transform: ctrl.getTranslate({
            x: ctrl.pendingChipX,
            y: ctrl.pendingChipY
          })
        }),
        config: _.partial(ctrl.configurePendingChip, ctrl, game)
      }) : null,
    // Bottom grid of slots (indicating space chips can occupy)
    m('div#chip-slots', _.times(grid.columnCount, function (c) {
      return m('div.grid-column', _.times(grid.rowCount, function (r) {
        return m('div.chip-slot', {
          class: classNames({
            'filled': grid.columns[c][r] !== undefined
          })
        });
      }));
    })),
    // Top grid of placed chips
    m('div#placed-chips', _.times(grid.columnCount, function (c) {
      return m('div.grid-column', _.map(grid.columns[c], function (chip, r) {
        return m('div', {
          key: 'chip-' + [c, r].join('-'),
          class: classNames(
            'chip',
            'placed',
            chip.player.color,
            {'highlighted': chip.highlighted}
          )
        });
      }));
    }))
  ]);
};

module.exports = GridComponent;
<commit_msg>Fix potential race condition with pending chip move<commit_after>'use strict';

var m = require('mithril');
var _ = require('underscore');
var classNames = require('classnames');
var Browser = require('../browser');

var GridComponent = {};

GridComponent.controller = function (game) {
  var ctrl = {
    // Initialize position of pending chip to the leftmost column
    pendingChipX: 0,
    pendingChipY: 0,
    // Booleans indicating when to transition the pending chip's movement in a
    // particular direction (for example, the pending chip should never
    // transition when resetting to its initial position after placing a chip)
    transitionPendingChipX: false,
    transitionPendingChipY: false,
    // Get the CSS translate string for the given coordinate map
    getTranslate: function (coords) {
      return 'translate(' + coords.x + 'px,' + coords.y + 'px)';
    },
    // Update the translation coordinates for the pending chip (this will take
    // effect on the next redraw)
    setPendingChipCoords: function (coords) {
      this.pendingChipX = coords.x;
      this.pendingChipY = coords.y;
    },
    // Retrieve the constant width of a single chip
    getChipWidth: function (grid) {
      var gridElem = document.getElementById('grid');
      return gridElem.offsetWidth / grid.columnCount;
    },
    // Get the index of the last visited column (the column where the cursor was
    // last at or where the last chip was dropped)
    getLastVisitedColumnIndex: function (grid, event) {
      var chipWidth = this.getChipWidth(grid);
      return Math.max(0, Math.floor((event.pageX - event.currentTarget.offsetLeft) / chipWidth));
    },
    // Move the pending chip to be aligned with the specified column
    movePendingChipToColumn: function (args) {
      // The last visited column is the grid column nearest to the cursor at
      // any given instant; keep track of the column's X position so the next
      // pending chip can instantaneously appear there
      this.lastVisitedColumnX = this.getChipWidth(args.game.grid) * args.column;
      this.setPendingChipCoords({
        x: this.lastVisitedColumnX,
        y: 0
      });
      this.transitionPendingChipX = true;
      this.transitionPendingChipY = false;
    },
    // Move the pending chip into alignment with the column nearest to the
    // user's cursor
    movePendingChipViaPointer: function (ctrl, game, event) {
      if (game.pendingChip && game.currentPlayer.type === 'human' && !ctrl.transitionPendingChipY) {
        var pointerColumnIndex = ctrl.getLastVisitedColumnIndex(game.grid, event);
        ctrl.movePendingChipToColumn({
          game: game,
          column: pointerColumnIndex
        });
      } else {
        m.redraw.strategy('none');
      }
    },
    // Get the coordinates of the chip slot element at the given column/row
    getSlotCoords: function (args) {
      var chipWidth = this.getChipWidth(args.grid);
      return {
        x: chipWidth * args.column,
        y: chipWidth * (args.grid.rowCount - args.row)
      };
    },
    // Place the pending chip into the specified column (or move the chip to
    // that column without placing it if the chip is not currently aligned with
    // the column)
    placePendingChip: function (args) {
      var rowIndex = args.game.grid.getNextAvailableSlot({
        column: args.column
      });
      // Do not allow user to place chip in column that is already full
      if (rowIndex === null) {
        m.redraw.strategy('none');
        return;
      }
      var slotCoords = this.getSlotCoords({
        grid: args.game.grid,
        column: args.column,
        row: rowIndex
      });
      // If pending chip is not currently aligned with chosen column
      if (this.pendingChipX !== slotCoords.x) {
        // First move pending chip into alignment with column
        this.movePendingChipToColumn({
          game: args.game,
          column: args.column
        });
        // Since AI players can't click to place a chip after the chip realigns
        // with the chosen column, place the chip automatically
        var ctrl = this;
        game.emitter.once('pending-chip:transition-end', function () {
          if (args.game.currentPlayer.type === 'AI') {
            args.game.currentPlayer.wait(function () {
              ctrl.placePendingChip(args);
            });
          }
        });
      } else {
        // Otherwise, chip is already aligned; drop chip into place on grid
        this.transitionPendingChipX = false;
        this.transitionPendingChipY = true;
        // Keep track of where chip was dropped
        this.lastVisitedColumnX = slotCoords.x;
        // Translate chip to the visual position on the grid corresponding to
        // the above column and row
        this.setPendingChipCoords(slotCoords);
        // Perform insertion on internal game grid once transition has ended
        this.finishPlacingPendingChip(args);
      }
      m.redraw();
    },
    // Place the pending chip into the column where the user clicked
    placePendingChipViaPointer: function (ctrl, game, event) {
      if (game.pendingChip && game.currentPlayer.type === 'human' && !ctrl.transitionPendingChipY) {
        ctrl.placePendingChip({
          game: game,
          column: ctrl.getLastVisitedColumnIndex(game.grid, event)
        });
      } else {
        m.redraw.strategy('none');
      }
    },
    // Actually insert the pending chip into the internal grid once the falling
    // transition has ended
    finishPlacingPendingChip: function (args) {
      var ctrl = this;
      game.emitter.once('pending-chip:transition-end', function finish() {
        args.game.placePendingChip({column: args.column});
        ctrl.transitionPendingChipX = false;
        ctrl.transitionPendingChipY = false;
        // Reset position of pending chip to the space directly above the last
        // visited column
        ctrl.setPendingChipCoords({
          x: ctrl.lastVisitedColumnX,
          y: 0
        });
        m.redraw();
      });
    },
    // Configure pending chip element when it's first created
    configurePendingChip: function (ctrl, game, pendingChipElem) {
      if (!ctrl.boundPendingChipTransitionEnd) {
        ctrl.boundPendingChipTransitionEnd = true;
        var eventName = Browser.normalizeEventName('transitionend');
        pendingChipElem.addEventListener(eventName, function () {
          game.emitter.emit('pending-chip:transition-end');
        });
      }
    }
  };
  // Place chip automatically when AI computes its next move on its turn
  game.emitter.on('ai-player:compute-next-move', function (chosenColumn) {
    ctrl.placePendingChip({
      game: game,
      column: chosenColumn
    });
  });
  return ctrl;
};
GridComponent.view = function (ctrl, game) {
  var grid = game.grid;
  return m('div#grid', {
    class: classNames({'has-winner': game.winner !== null}),
    onmousemove: _.partial(ctrl.movePendingChipViaPointer, ctrl, game),
    onclick: _.partial(ctrl.placePendingChipViaPointer, ctrl, game)
  }, [
    // The chip that is about to be placed on the grid
    game.pendingChip ?
      m('div', {
        class: classNames(
          'chip',
          'pending',
          game.pendingChip.player.color,
           {'transition-x': ctrl.transitionPendingChipX},
           {'transition-y': ctrl.transitionPendingChipY}
        ),
        style: Browser.normalizeStyles({
          transform: ctrl.getTranslate({
            x: ctrl.pendingChipX,
            y: ctrl.pendingChipY
          })
        }),
        config: _.partial(ctrl.configurePendingChip, ctrl, game)
      }) : null,
    // Bottom grid of slots (indicating space chips can occupy)
    m('div#chip-slots', _.times(grid.columnCount, function (c) {
      return m('div.grid-column', _.times(grid.rowCount, function (r) {
        return m('div.chip-slot', {
          class: classNames({
            'filled': grid.columns[c][r] !== undefined
          })
        });
      }));
    })),
    // Top grid of placed chips
    m('div#placed-chips', _.times(grid.columnCount, function (c) {
      return m('div.grid-column', _.map(grid.columns[c], function (chip, r) {
        return m('div', {
          key: 'chip-' + [c, r].join('-'),
          class: classNames(
            'chip',
            'placed',
            chip.player.color,
            {'highlighted': chip.highlighted}
          )
        });
      }));
    }))
  ]);
};

module.exports = GridComponent;
<|endoftext|>


<commit_before>  /*!
  * ----------------------
  * Keen IO + Chartstack
  * ----------------------
  */

  !function(name, context){
    var Keen = context[name] || {};
    var cs = context.chartstack;
    Keen.vis = cs; //.noConflict();
    Keen.Chart = function(obj){
      return new Keen.vis.Chart(obj);
    };
    Keen.ready = Keen.vis.ready;
    Keen.vis.addAdapter('default', Keen.vis.adapters['keen-io']);
    Keen.palette = [
      '#00afd7', // blue
      '#49c5b1', // green
      '#e6b449', // gold
      '#f35757'  // red
    ];

    // -------------------------------
    // Inject Draw Methods
    // -------------------------------

    Keen.prototype.draw = function(query, selector, config) {
      var stub = new Keen.Request(this, [query]);
      return new Keen.Visualization(stub, selector, config);
    };

    Keen.Request.prototype.draw = function(selector, config) {
      if (!this.visual) {
        this.visual = new Keen.Visualization(this, selector, config);
        this.on("complete", function(){
          this.visual.dataset.responses = [this.data];
          this.visual.dataset.transform();
        })
      }
      return this;
    };

    // -------------------------------
    // Keen.Visualization
    // -------------------------------

    Keen.Visualization = function(req, selector, config){
      var options = (config || {}), recommended;
      var library = Keen.vis.libraries[options.library] || Keen.vis.library;

      var datasetConfig = {
        adapter: "keen-io",
        url: req.instance.client.endpoint + '/projects/' + req.instance.client.projectId + req.queries[0].path,
        dateformat: config.dateFormat || "",
        params: req.queries[0].params
      };
      datasetConfig.params.api_key = req.instance.client.readKey;

      var viewConfig = {
        el: selector,
        chartOptions: config.chartOptions || {}
      };
      viewConfig.chartOptions.colors = viewConfig.chartOptions.colors || Keen.palette;

      if (req.queries[0].params.interval) { // Series
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'linechart';
      } else {
        if (req.queries[0].params.group_by) { // Static
          options.capable = ['piechart', 'barchart', 'columnchart', 'table'];
          recommended = 'barchart';
        } else { // Metric
          options.capable = ['metric'];
          recommended = 'metric';
        }
      }

      // 2x Group_by handler
      if (req.queries[0].params.group_by instanceof Array) {
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'columnchart';
        datasetConfig.map = {
          root: 'result',
          //each: {},
          sort: {
            index: 'asc',
            label: 'desc'
          }
        };
        if (req.queries[0].params.interval) {
          datasetConfig.map.each = {
            label: 'value -> ' + req.queries[0].params.group_by[0],
            index: 'timeframe -> start',
            value: 'value -> result'
          };
        } else {
          datasetConfig.map.each = {
            label: req.queries[0].params.group_by[1],
            index: req.queries[0].params.group_by[0],
            value: 'result'
          };
        }
      }

      if (req.queries[0].params.steps) {
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'columnchart';
        /*datasetConfig.map = {
          root: '',
          each: {
            //index: 'steps -> event_collection',
            label: 'steps -> event_collection',
            value: 'result'
          },
          sort: {
            index: 'asc'
            //label: 'desc'
          }
        };*/
      }

      options.chartType = options.chartType || recommended;

      if (options.chartType == 'metric') {
        library = 'keen-io';
      }

      if (library) {
        if (Keen.vis.libraries[library][options.chartType]) {
          return new Keen.Chart({
            dataset: new Keen.vis.Dataset(datasetConfig),
            view: new Keen.vis.libraries[library][options.chartType](viewConfig)
          })

        } else {
          Keen.log('The visualization type you requested is not available for this library');
        }
      } else {
        //console.log(req.queries[0].params.group_by instanceof Array);
        Keen.log('The visualization library you requested is not present');
      }

      return this;
    };
  }('Keen', this);
<commit_msg>Update color defaults<commit_after>  /*!
  * ----------------------
  * Keen IO + Chartstack
  * ----------------------
  */

  !function(name, context){
    var Keen = context[name] || {};
    var cs = context.chartstack;
    Keen.vis = cs; //.noConflict();
    Keen.Chart = function(obj){
      return new Keen.vis.Chart(obj);
    };
    Keen.ready = Keen.vis.ready;
    Keen.vis.addAdapter('default', Keen.vis.adapters['keen-io']);

    Keen.vis.defaults = Keen.vis.defaults || {};
    Keen.vis.defaults.colors = [
      '#00afd7', // blue
      '#49c5b1', // green
      '#e6b449', // gold
      '#f35757'  // red
    ];

    // -------------------------------
    // Inject Draw Methods
    // -------------------------------

    Keen.prototype.draw = function(query, selector, config) {
      var stub = new Keen.Request(this, [query]);
      return new Keen.Visualization(stub, selector, config);
    };

    Keen.Request.prototype.draw = function(selector, config) {
      if (!this.visual) {
        this.visual = new Keen.Visualization(this, selector, config);
        this.on("complete", function(){
          this.visual.dataset.responses = [this.data];
          this.visual.dataset.transform();
        })
      }
      return this;
    };

    // -------------------------------
    // Keen.Visualization
    // -------------------------------

    Keen.Visualization = function(req, selector, config){
      var options = (config || {}), recommended;
      var library = Keen.vis.libraries[options.library] || Keen.vis.library;

      var datasetConfig = {
        adapter: "keen-io",
        url: req.instance.client.endpoint + '/projects/' + req.instance.client.projectId + req.queries[0].path,
        dateformat: config.dateFormat || "",
        params: req.queries[0].params
      };
      datasetConfig.params.api_key = req.instance.client.readKey;

      var viewConfig = {
        el: selector,
        chartOptions: config.chartOptions || {}
      };
      viewConfig.chartOptions.colors = viewConfig.chartOptions.colors || Keen.vis.defaults.colors;

      if (req.queries[0].params.interval) { // Series
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'linechart';
      } else {
        if (req.queries[0].params.group_by) { // Static
          options.capable = ['piechart', 'barchart', 'columnchart', 'table'];
          recommended = 'barchart';
        } else { // Metric
          options.capable = ['metric'];
          recommended = 'metric';
        }
      }

      // 2x Group_by handler
      if (req.queries[0].params.group_by instanceof Array) {
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'columnchart';
        datasetConfig.map = {
          root: 'result',
          //each: {},
          sort: {
            index: 'asc',
            label: 'desc'
          }
        };
        if (req.queries[0].params.interval) {
          datasetConfig.map.each = {
            label: 'value -> ' + req.queries[0].params.group_by[0],
            index: 'timeframe -> start',
            value: 'value -> result'
          };
        } else {
          datasetConfig.map.each = {
            label: req.queries[0].params.group_by[1],
            index: req.queries[0].params.group_by[0],
            value: 'result'
          };
        }
      }

      if (req.queries[0].params.steps) {
        options.capable = ['areachart', 'barchart', 'columnchart', 'linechart', 'table'];
        recommended = 'columnchart';
        /*datasetConfig.map = {
          root: '',
          each: {
            //index: 'steps -> event_collection',
            label: 'steps -> event_collection',
            value: 'result'
          },
          sort: {
            index: 'asc'
            //label: 'desc'
          }
        };*/
      }

      options.chartType = options.chartType || recommended;

      if (options.chartType == 'metric') {
        library = 'keen-io';
      }

      if (library) {
        if (Keen.vis.libraries[library][options.chartType]) {
          return new Keen.Chart({
            dataset: new Keen.vis.Dataset(datasetConfig),
            view: new Keen.vis.libraries[library][options.chartType](viewConfig)
          })

        } else {
          Keen.log('The visualization type you requested is not available for this library');
        }
      } else {
        //console.log(req.queries[0].params.group_by instanceof Array);
        Keen.log('The visualization library you requested is not present');
      }

      return this;
    };
  }('Keen', this);
<|endoftext|>


<commit_before>import { taffy }  from 'taffydb';

/**
 * Provides several utility methods and event bindings for the TJSDoc document / tag data using TaffyDB.
 *
 * An onPluginLoad callback also wires up DocDB to an eventbus via {@link PluginManager}.
 */
export class DocDB
{
   /**
    * Initializes the TaffyDB instance with given document data.
    *
    * @param {DocObject[]} [docData] - DocObject data.
    */
   constructor(docData = void 0)
   {
      /**
       * TaffyDB instance of docData.
       * @type {Taffy}
       */
      this._docDB = taffy(docData);

      this._docID = 0; // TODO determine highest __docId__ from any given docData
   }

   /**
    * Find doc object with given filter options.
    *
    * @param {...Object} cond - find condition.
    *
    * @returns {DocObject[]} found doc objects.
    */
   find(...cond)
   {
      return this.findSorted(null, ...cond);
   }

   /**
    * find doc object for each access.
    *
    * @param {DocObject}   doc - parent doc object.
    * @param {string}      kind - kind property condition.
    * @param {boolean}     [isStatic=true] - static property condition
    *
    * @returns {*[]} found doc objects.
    * ```
    * (Array[]) 0 - ['Public', DocObject[]]
    * (Array[]) 1 - ['Protected', DocObject[]]
    * (Array[]) 2 - ['Private', DocObject[]]
    * ```
    */
   findAccessDocs(doc, kind, isStatic = true)
   {
      const cond = { kind, 'static': isStatic };

      if (doc) { cond.memberof = doc.longname; }

      /* eslint-disable default-case */
      switch (kind)
      {
         case 'class':
            cond.interface = false;
            break;

         case 'interface':
            cond.kind = 'class';
            cond.interface = true;
            break;

         case 'member':
            cond.kind = ['member', 'get', 'set'];
            break;
      }

      const publicDocs = this.find(cond, { access: 'public' }).filter((v) => !v.builtinVirtual);
      const protectedDocs = this.find(cond, { access: 'protected' }).filter((v) => !v.builtinVirtual);
      const privateDocs = this.find(cond, { access: 'private' }).filter((v) => !v.builtinVirtual);

      // access docs
      return [['Public', publicDocs], ['Protected', protectedDocs], ['Private', privateDocs]];
   }

   /**
    * Find all identifiers with grouping by kind.
    *
    * @returns {{class: DocObject[], interface: DocObject[], function: DocObject[], variable: DocObject[], typedef: DocObject[], external: DocObject[]}} found doc objects.
    */
   findAllIdentifiersKindGrouping()
   {
      return {
         'class': this.find([{ 'kind': 'class', 'interface': false }]),
         'interface': this.find([{ 'kind': 'class', 'interface': true }]),
         'function': this.find([{ kind: 'function' }]),
         'variable': this.find([{ kind: 'variable' }]),
         'typedef': this.find([{ kind: 'typedef' }]),
         'external': this.find([{ kind: 'external' }]).filter((v) => !v.builtinVirtual)
      };
   }

   /**
    * Fuzzy find doc object by name.
    * - equal with longname
    * - equal with name
    * - included in longname
    * - included in ancestor
    *
    * @param {string} name - Target identifier name.
    * @param {string} [kind] - Target kind.
    *
    * @returns {DocObject[]} found doc objects.
    */
   findByName(name, kind = void 0)
   {
      let docs;

      docs = this.findSorted(null, kind ? { longname: name, kind } : { longname: name });

      if (docs.length) { return docs; }

      docs = this.findSorted(null, kind ? { name, kind } : { name });

      if (docs.length) { return docs; }

      const regex = new RegExp(`[~]${name.replace('*', '\\*')}$`); // if name is `*`, need to escape.

      docs = this.findSorted(null, kind ? { longname: { regex }, kind } : { longname: { regex } });

      if (docs.length) { return docs; }

      // inherited method?
      const matched = name.match(/(.*)[.#](.*)$/); // instance method(Foo#bar) or static method(Foo.baz)

      if (matched)
      {
         const parent = matched[1];
         const childName = matched[2];
         const parentDoc = this.findByName(parent, 'class')[0];

         if (parentDoc && parentDoc._custom_extends_chains)
         {
            for (const superLongname of parentDoc._custom_extends_chains)
            {
               if (docs.length) { return this.find({ memberof: superLongname, name: childName }); }  // docs
            }
         }
      }

      return [];
   }

   /**
    * Find doc objects sorted by name and any optional sorting criteria passed in as the first parameter.
    *
    * @param {string}      [order] - doc objects order(``column asec`` or ``column desc``).
    * @param {...Object}   [cond] - condition objects - A TaffyDB filter query.
    *
    * @returns {DocObject[]} found doc objects.
    */
   findSorted(order = void 0, ...cond)
   {
      const data = this._docDB(...cond);

      return data.order(order ? `${order}, name asec` : 'name asec').map((v) => v);
   }

   /**
    * Returns the current doc ID.
    *
    * @returns {number}
    */
   getCurrentID()
   {
      return this._docID;
   }

   /**
    * Returns the current doc ID then post-increments it.
    *
    * @returns {number}
    */
   getCurrentIDAndIncrement()
   {
      return this._docID++;
   }

   /**
    * Inserts an object, array of objects, or a DocDB into this instance.
    *
    * @param {DocObject|DocObject[]|DocDB}   objectOrDB - A single instance or array of DocObjects or DocDB to merge.
    *
    * @returns {Taffy}
    */
   insert(objectOrDB)
   {
      if (objectOrDB instanceof DocDB)
      {
         if (objectOrDB === this._docDB)
         {
            throw new ReferenceError(`'objectOrDB' is the same instance as this DocDB.`);
         }

         return this._docDB.insert(objectOrDB.find());
      }
      else if (typeof objectOrDB === 'object')
      {
         return this._docDB.insert(objectOrDB);
      }

      throw new ReferenceError(`'objectOrDB' is not an 'object' or 'array'.`);
   }

   /**
    * Invokes the `onHandleDocObject` plugin callback for each DocObject given before inserting into this DocDB
    * instance. The DocObject is by default destroyed upon insertion. This allows it to go out of scope. Before
    * insertion into the DocDB the doc value is filtered removing any unnecessary data such as AST content
    * based on the target project TJSDocConfig instance.
    *
    * @param {DocObject}   docObject - DocObject to insert.
    *
    * @param {boolean}     destroy - Destroys the DocObject removing all internal data references so that it can go
    *                                out of scope.
    * @returns {Taffy}
    * @private
    */
   insertDocObject(docObject, destroy = true)
   {
      const doc = docObject.value;

      if (this._eventbus)
      {
         this._eventbus.trigger('plugins:invoke:sync:event', 'onHandleDocObject', void 0, { doc });
      }

      // Destroys the docObject.
      if (destroy) { docObject.destroy(); }

      // Filter out any unnecessary based on the target project TJSDocConfig.
      if (this._config)
      {
         switch (doc.kind)
         {
            case 'file':
               // Filter out any AST data as it is not further processed.
               if (!this._config.outputASTData) { delete doc.node; }

               // Filter out any file content.
               if (!this._config.includeSource) { doc.content = ''; }
               break;

            case 'testFile':
               // Filter out any file content.
               if (!this._config.includeSource) { doc.content = ''; }
               break;

            // Catch all for any unmatched doc kind above.
            default:
               // Filter out any AST data as it is not further processed.
               if (!this._config.outputASTData) { delete doc.node; }
               break;
         }
      }

      // Inserts the doc object into the TaffyDB instance.
      return this._docDB.insert(doc);
   }

   /**
    * Merges an object, array of objects, or a DocDB into this instance with a default identity column of `id`. The
    * given key if defines the identity column for the merge.
    *
    * @param {DocObject|DocObject[]|DocDB}   objectOrDB - A single instance or array of DocObjects or DocDB to merge.
    *
    * @param {*}                             key - Identity column to be used to match records against the existing
    *                                              db. The TaffyDB default is: `id`.
    *
    * @returns {Taffy}
    */
   merge(objectOrDB, key = void 0)
   {
      if (objectOrDB instanceof DocDB)
      {
         if (objectOrDB === this._docDB)
         {
            throw new ReferenceError(`'objectOrDB' is the same instance as this DocDB.`);
         }

         return this._docDB.merge(objectOrDB.find(), key);
      }
      else if (typeof objectOrDB === 'object')
      {
         return this._docDB.merge(objectOrDB, key);
      }

      throw new ReferenceError(`'objectOrDB' is not an 'object' or 'array'.`);
   }

   /**
    * Wires up DocDB to plugin eventbus.
    *
    * @param {PluginEvent} ev - The plugin event.
    *
    * @ignore
    */
   onPluginLoad(ev)
   {
      this._eventbus = ev.eventbus;

      this._config = this._eventbus.triggerSync('tjsdoc:data:config:get');

      let eventPrepend = 'tjsdoc';

      // If `eventPrepend` is defined then it is prepended before all event bindings.
      if (typeof ev.pluginOptions.eventPrepend === 'string') { eventPrepend = `${ev.pluginOptions.eventPrepend}`; }

      this._eventbus.on(`${eventPrepend}:data:docdb:current:id:get`, this.getCurrentID, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:current:id:increment:get`, this.getCurrentIDAndIncrement, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find`, this.find, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:access:docs`, this.findAccessDocs, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:all:identifiers:kind:grouping`,
       this.findAllIdentifiersKindGrouping, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:by:name`, this.findByName, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:sorted`, this.findSorted, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:get`, () => this, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:insert:doc:object`, this.insertDocObject, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:insert`, this.insert, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:merge`, this.merge, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:query`, this.query, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:reset`, this.reset, this);
   }

   /**
    * Performs a TaffyDB query.
    *
    * @param {object|undefined}  [query] - An optional TaffyDB query.
    *
    * @see http://www.taffydb.com/
    * @returns {Taffy}
    */
   query(query = void 0)
   {
      return this._docDB(query);
   }

   /**
    * Resets the DocDB.
    */
   reset()
   {
      this._docDB().remove();
      this._docID = 0;
   }
}

/**
 * Adds event binding to create a DocDB instance from docData / DocObject[].
 *
 * @param {PluginEvent} ev - The plugin event.
 */
export function onPluginLoad(ev)
{
   const eventbus = ev.eventbus;

   eventbus.on('tjsdoc:system:docdb:create', (docData) => new DocDB(docData));
}
<commit_msg>added remove and removeDB<commit_after>import { taffy }  from 'taffydb';

/**
 * Provides several utility methods and event bindings for the TJSDoc document / tag data using TaffyDB.
 *
 * An onPluginLoad callback also wires up DocDB to an eventbus via {@link PluginManager}.
 */
export class DocDB
{
   /**
    * Initializes the TaffyDB instance with given document data.
    *
    * @param {DocObject[]} [docData] - DocObject data.
    */
   constructor(docData = void 0)
   {
      /**
       * TaffyDB instance of docData.
       * @type {Taffy}
       */
      this._docDB = taffy(docData);

      this._docID = 0; // TODO determine highest __docId__ from any given docData
   }

   /**
    * Find doc object with given filter options.
    *
    * @param {...Object} cond - find condition.
    *
    * @returns {DocObject[]} found doc objects.
    */
   find(...cond)
   {
      return this.findSorted(null, ...cond);
   }

   /**
    * find doc object for each access.
    *
    * @param {DocObject}   doc - parent doc object.
    * @param {string}      kind - kind property condition.
    * @param {boolean}     [isStatic=true] - static property condition
    *
    * @returns {*[]} found doc objects.
    * ```
    * (Array[]) 0 - ['Public', DocObject[]]
    * (Array[]) 1 - ['Protected', DocObject[]]
    * (Array[]) 2 - ['Private', DocObject[]]
    * ```
    */
   findAccessDocs(doc, kind, isStatic = true)
   {
      const cond = { kind, 'static': isStatic };

      if (doc) { cond.memberof = doc.longname; }

      /* eslint-disable default-case */
      switch (kind)
      {
         case 'class':
            cond.interface = false;
            break;

         case 'interface':
            cond.kind = 'class';
            cond.interface = true;
            break;

         case 'member':
            cond.kind = ['member', 'get', 'set'];
            break;
      }

      const publicDocs = this.find(cond, { access: 'public' }).filter((v) => !v.builtinVirtual);
      const protectedDocs = this.find(cond, { access: 'protected' }).filter((v) => !v.builtinVirtual);
      const privateDocs = this.find(cond, { access: 'private' }).filter((v) => !v.builtinVirtual);

      // access docs
      return [['Public', publicDocs], ['Protected', protectedDocs], ['Private', privateDocs]];
   }

   /**
    * Find all identifiers with grouping by kind.
    *
    * @returns {{class: DocObject[], interface: DocObject[], function: DocObject[], variable: DocObject[], typedef: DocObject[], external: DocObject[]}} found doc objects.
    */
   findAllIdentifiersKindGrouping()
   {
      return {
         'class': this.find([{ 'kind': 'class', 'interface': false }]),
         'interface': this.find([{ 'kind': 'class', 'interface': true }]),
         'function': this.find([{ kind: 'function' }]),
         'variable': this.find([{ kind: 'variable' }]),
         'typedef': this.find([{ kind: 'typedef' }]),
         'external': this.find([{ kind: 'external' }]).filter((v) => !v.builtinVirtual)
      };
   }

   /**
    * Fuzzy find doc object by name.
    * - equal with longname
    * - equal with name
    * - included in longname
    * - included in ancestor
    *
    * @param {string} name - Target identifier name.
    * @param {string} [kind] - Target kind.
    *
    * @returns {DocObject[]} found doc objects.
    */
   findByName(name, kind = void 0)
   {
      let docs;

      docs = this.findSorted(null, kind ? { longname: name, kind } : { longname: name });

      if (docs.length) { return docs; }

      docs = this.findSorted(null, kind ? { name, kind } : { name });

      if (docs.length) { return docs; }

      const regex = new RegExp(`[~]${name.replace('*', '\\*')}$`); // if name is `*`, need to escape.

      docs = this.findSorted(null, kind ? { longname: { regex }, kind } : { longname: { regex } });

      if (docs.length) { return docs; }

      // inherited method?
      const matched = name.match(/(.*)[.#](.*)$/); // instance method(Foo#bar) or static method(Foo.baz)

      if (matched)
      {
         const parent = matched[1];
         const childName = matched[2];
         const parentDoc = this.findByName(parent, 'class')[0];

         if (parentDoc && parentDoc._custom_extends_chains)
         {
            for (const superLongname of parentDoc._custom_extends_chains)
            {
               if (docs.length) { return this.find({ memberof: superLongname, name: childName }); }  // docs
            }
         }
      }

      return [];
   }

   /**
    * Find doc objects sorted by name and any optional sorting criteria passed in as the first parameter.
    *
    * @param {string}      [order] - doc objects order(``column asec`` or ``column desc``).
    * @param {...Object}   [cond] - condition objects - A TaffyDB filter query.
    *
    * @returns {DocObject[]} found doc objects.
    */
   findSorted(order = void 0, ...cond)
   {
      const data = this._docDB(...cond);

      return data.order(order ? `${order}, name asec` : 'name asec').map((v) => v);
   }

   /**
    * Returns the current doc ID.
    *
    * @returns {number}
    */
   getCurrentID()
   {
      return this._docID;
   }

   /**
    * Returns the current doc ID then post-increments it.
    *
    * @returns {number}
    */
   getCurrentIDAndIncrement()
   {
      return this._docID++;
   }

   /**
    * Inserts an object, array of objects, or a DocDB into this instance.
    *
    * @param {DocObject|DocObject[]|DocDB}   objectOrDB - A single instance or array of DocObjects or DocDB to merge.
    *
    * @returns {Taffy}
    */
   insert(objectOrDB)
   {
      if (objectOrDB instanceof DocDB)
      {
         if (objectOrDB === this._docDB)
         {
            throw new ReferenceError(`'objectOrDB' is the same instance as this DocDB.`);
         }

         return this._docDB.insert(objectOrDB.find());
      }
      else if (typeof objectOrDB === 'object')
      {
         return this._docDB.insert(objectOrDB);
      }

      throw new ReferenceError(`'objectOrDB' is not an 'object' or 'array'.`);
   }

   /**
    * Invokes the `onHandleDocObject` plugin callback for each DocObject given before inserting into this DocDB
    * instance. The DocObject is by default destroyed upon insertion. This allows it to go out of scope. Before
    * insertion into the DocDB the doc value is filtered removing any unnecessary data such as AST content
    * based on the target project TJSDocConfig instance.
    *
    * @param {DocObject}   docObject - DocObject to insert.
    *
    * @param {boolean}     destroy - Destroys the DocObject removing all internal data references so that it can go
    *                                out of scope.
    * @returns {Taffy}
    * @private
    */
   insertDocObject(docObject, destroy = true)
   {
      const doc = docObject.value;

      if (this._eventbus)
      {
         this._eventbus.trigger('plugins:invoke:sync:event', 'onHandleDocObject', void 0, { doc });
      }

      // Destroys the docObject.
      if (destroy) { docObject.destroy(); }

      // Filter out any unnecessary based on the target project TJSDocConfig.
      if (this._config)
      {
         switch (doc.kind)
         {
            case 'file':
               // Filter out any AST data as it is not further processed.
               if (!this._config.outputASTData) { delete doc.node; }

               // Filter out any file content.
               if (!this._config.includeSource) { doc.content = ''; }
               break;

            case 'testFile':
               // Filter out any file content.
               if (!this._config.includeSource) { doc.content = ''; }
               break;

            // Catch all for any unmatched doc kind above.
            default:
               // Filter out any AST data as it is not further processed.
               if (!this._config.outputASTData) { delete doc.node; }
               break;
         }
      }

      // Inserts the doc object into the TaffyDB instance.
      return this._docDB.insert(doc);
   }

   /**
    * Merges an object, array of objects, or a DocDB into this instance with a default identity column of `id`. The
    * given key if defines the identity column for the merge.
    *
    * @param {DocObject|DocObject[]|DocDB}   objectOrDB - A single instance or array of DocObjects or DocDB to merge.
    *
    * @param {*}                             key - Identity column to be used to match records against the existing
    *                                              db. The TaffyDB default is: `id`.
    *
    * @returns {Taffy}
    */
   merge(objectOrDB, key = void 0)
   {
      if (objectOrDB instanceof DocDB)
      {
         if (objectOrDB === this._docDB)
         {
            throw new ReferenceError(`'objectOrDB' is the same instance as this DocDB.`);
         }

         return this._docDB.merge(objectOrDB.find(), key);
      }
      else if (typeof objectOrDB === 'object')
      {
         return this._docDB.merge(objectOrDB, key);
      }

      throw new ReferenceError(`'objectOrDB' is not an 'object' or 'array'.`);
   }

   /**
    * Wires up DocDB to plugin eventbus.
    *
    * @param {PluginEvent} ev - The plugin event.
    *
    * @ignore
    */
   onPluginLoad(ev)
   {
      this._eventbus = ev.eventbus;

      this._config = this._eventbus.triggerSync('tjsdoc:data:config:get');

      let eventPrepend = 'tjsdoc';

      // If `eventPrepend` is defined then it is prepended before all event bindings.
      if (typeof ev.pluginOptions.eventPrepend === 'string') { eventPrepend = `${ev.pluginOptions.eventPrepend}`; }

      this._eventbus.on(`${eventPrepend}:data:docdb:current:id:get`, this.getCurrentID, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:current:id:increment:get`, this.getCurrentIDAndIncrement, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find`, this.find, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:access:docs`, this.findAccessDocs, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:all:identifiers:kind:grouping`,
       this.findAllIdentifiersKindGrouping, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:by:name`, this.findByName, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:find:sorted`, this.findSorted, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:get`, () => this, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:insert:doc:object`, this.insertDocObject, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:insert`, this.insert, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:merge`, this.merge, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:query`, this.query, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:remove`, this.remove, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:remove:db`, this.removeDB, this);
      this._eventbus.on(`${eventPrepend}:data:docdb:reset`, this.reset, this);
   }

   /**
    * Performs a TaffyDB query.
    *
    * @param {Array|function|object|string}  [query] - An optional TaffyDB query; one or more Strings, records, filter
    *                                                  objects, arrays, or functions.
    *
    * @see http://www.taffydb.com/
    * @returns {Taffy}
    */
   query(...query)
   {
      return this._docDB(...query);
   }

   /**
    * Removes docs that match the query.
    *
    * @param {Array|function|object|string|undefined} [query] - An optional TaffyDB query; one or more Strings, records,
    *                                                           filter objects, arrays, or functions.
    * @returns {number} - count of docs removed.
    */
   remove(...query)
   {
      return this._docDB(...query).remove();
   }

   /**
    * Removes all docs by `filePath` in this instance that are found in the given DocDB.
    *
    * @param {DocDB} docDB - The source DB to query for doc file paths to remove.
    *
    * @returns {number} - count of docs removed.
    */
   removeDB(docDB)
   {
      if (!(docDB instanceof DocDB)) { throw new TypeError(`'docDB' is not a 'DocDB'.`); }

      const distinctPaths = docDB.query().distinct('filePath');

      return distinctPaths.length > 0 ? this._docDB({ filePath: distinctPaths }).remove() : 0;
   }

   /**
    * Resets the DocDB.
    */
   reset()
   {
      this._docDB().remove();
      this._docID = 0;
   }
}

/**
 * Adds event binding to create a DocDB instance from docData / DocObject[].
 *
 * @param {PluginEvent} ev - The plugin event.
 */
export function onPluginLoad(ev)
{
   const eventbus = ev.eventbus;

   eventbus.on('tjsdoc:system:docdb:create', (docData) => new DocDB(docData));
}
<|endoftext|>


<commit_before>'use strict';

// modules dependencies
var mongoat = require('../../index');

var _this;
var id;

// test update method
describe('Versioning', function () {
  // connect to db before all tests
  beforeAll(function (done) {
    _this = this;

    mongoat.MongoClient.connect('mongodb://localhost:27017/mongoatTest')
    .then(function (db) {
      db.dropDatabase();
      _this.testDb = db;
      _this.testCol = db.collection('Person.verioning');
      _this.testCol.datetime(true);
      _this.testCol.version(true);

      // add before update hooks
      _this.testCol.before('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      _this.testCol.before('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      // add after update hooks
      _this.testCol.after('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      _this.testCol.after('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      done();
    });
  });

  // close db after all tests
  afterAll(function (done) {
    _this.testCol = _this.testDb.collection('Person.verioning.vermongo');

    _this.testCol.find()
    .toArray()
    .then(function (mongoArray) {
      expect(mongoArray.length).toBe(7);

      _this.testDb.dropDatabase();
      _this.testDb.close();

      done();
    });
  });

  // test restore
  it('should throw error, because of id undefined',
  function (done) {
    try {
      _this.testCol.restore();
    } catch(err) {
      expect(err.message).toBe('The provided id is null or undefined');
      done();
    }
  });

  // test insert
  it('should insert new document to collection',
  function (done) {
    _this.testCol.insert({
      firstName: 'Yacine',
      lastName: 'KHATAL',
      age: 25
    }).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      id = mongObject.ops[0]._id;

      done();
    });
  });

  // test upsert
  it('should upsert new document to collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Hichem' },
      { $setOnInsert: { lastName: 'KHATAL', age: 28 } },
      { upsert: true }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(0);

      done();
    });
  });

  // test update
  it('should update document from collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { age: 30, company: 'Dial Once' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

      done();
    });
  });

  // test update
  it('should update document from collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { age: 35, job: 'software engineer' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

      done();
    });
  });

  // test restore
  it('should throw error',
  function (done) {
    _this.testCol.restore(id, -7)
    .catch(function (err) {
      expect(err.message).toBe('The requested version doesn\'t exist');

      done();
    });
  });

  // test restore
  it('should restore version 2 document by version',
  function (done) {
    _this.testCol.restore(id, 2)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(30);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBeUndefined();

      done();
    });
  });

  // test restore
  it('should restore version 3 document by version',
  function (done) {
    _this.testCol.restore(id, 3)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(35);
      expect(mongObject.job).toBe('software engineer');
      expect(mongObject.company).toBe('Dial Once');

      done();
    });
  });

  // test restore
  it('should restore last version of the document by version',
  function (done) {
    _this.testCol.restore(id, 0)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(30);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBeUndefined();

      done();
    });
  });

  // test restore
  it('should restore version -2 of the document by version',
  function (done) {
    _this.testCol.restore(id, -4)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(25);
      expect(mongObject.job).toBeUndefined();
      expect(mongObject.company).toBeUndefined();

      done();
    });
  });

  // test remove
  it('should remove document from collection',
  function (done) {
    _this.testCol.remove({ firstName: 'Yacine' })
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);

      done();
    });
  });

  // test restore
  it('should restore lest version',
  function (done) {
    _this.testCol.restore(id)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(25);
      expect(mongObject.job).toBeUndefined();
      expect(mongObject.company).toBeUndefined();

      done();
    });
  });
});
<commit_msg>Enhance tests to ensure that version is well setted<commit_after>'use strict';

// modules dependencies
var mongoat = require('../../index');

var _this;
var id;

// test update method
describe('Versioning', function () {
  // connect to db before all tests
  beforeAll(function (done) {
    _this = this;

    mongoat.MongoClient.connect('mongodb://localhost:27017/mongoatTest')
    .then(function (db) {
      db.dropDatabase();
      _this.testDb = db;
      _this.testCol = db.collection('Person.verioning');
      _this.testCol.datetime(true);
      _this.testCol.version(true);

      // add before update hooks
      _this.testCol.before('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      _this.testCol.before('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      // add after update hooks
      _this.testCol.after('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      _this.testCol.after('update', function (object) {
        expect(typeof object).toBe('object');
        return object;
      });

      done();
    });
  });

  // close db after all tests
  afterAll(function (done) {
    _this.testCol = _this.testDb.collection('Person.verioning.vermongo');

    _this.testCol.find()
    .toArray()
    .then(function (mongoArray) {
      expect(mongoArray.length).toBe(7);

      _this.testDb.dropDatabase();
      _this.testDb.close();

      done();
    });
  });

  // test restore
  it('should throw error, because of id undefined',
  function (done) {
    try {
      _this.testCol.restore();
    } catch(err) {
      expect(err.message).toBe('The provided id is null or undefined');
      done();
    }
  });

  // test insert
  it('should insert new document to collection',
  function (done) {
    _this.testCol.insert({
      firstName: 'Yacine',
      lastName: 'KHATAL',
      age: 25
    }).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      id = mongObject.ops[0]._id;

      done();
    });
  });

  // test upsert
  it('should upsert new document to collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Hichem' },
      { $setOnInsert: { lastName: 'KHATAL', age: 28 } },
      { upsert: true }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(0);

      done();
    });
  });

  // test update
  it('should update document from collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { age: 30, company: 'Dial Once' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

      done();
    });
  });

  // test update
  it('should update document from collection',
  function (done) {
    _this.testCol.update(
      { firstName: 'Yacine' },
      { $set: { age: 35, job: 'software engineer' } }
    ).then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);
      expect(mongObject.result.nModified).toBe(1);

      done();
    });
  });

  // test restore
  it('should throw error',
  function (done) {
    _this.testCol.restore(id, -7)
    .catch(function (err) {
      expect(err.message).toBe('The requested version doesn\'t exist');

      done();
    });
  });

  // test restore
  it('should restore version 2 document by version',
  function (done) {
    _this.testCol.restore(id, 2)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(30);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBeUndefined();
      expect(mongObject._version).toBe(4);

      done();
    });
  });

  // test restore
  it('should restore version 3 document by version',
  function (done) {
    _this.testCol.restore(id, 3)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(35);
      expect(mongObject.job).toBe('software engineer');
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject._version).toBe(5);

      done();
    });
  });

  // test restore
  it('should restore last version of the document by version',
  function (done) {
    _this.testCol.restore(id, 0)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(30);
      expect(mongObject.company).toBe('Dial Once');
      expect(mongObject.job).toBeUndefined();
      expect(mongObject._version).toBe(6);

      done();
    });
  });

  // test restore
  it('should restore version -2 of the document by version',
  function (done) {
    _this.testCol.restore(id, -4)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(25);
      expect(mongObject.job).toBeUndefined();
      expect(mongObject.company).toBeUndefined();
      expect(mongObject._version).toBe(7);

      done();
    });
  });

  // test remove
  it('should remove document from collection',
  function (done) {
    _this.testCol.remove({ firstName: 'Yacine' })
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(typeof mongObject.result).toBe('object');
      expect(mongObject.result.ok).toBe(1);
      expect(mongObject.result.n).toBe(1);

      done();
    });
  });

  // test restore
  it('should restore lest version',
  function (done) {
    _this.testCol.restore(id)
    .then(function (mongObject) {
      expect(typeof mongObject).toBe('object');
      expect(mongObject.firstName).toBe('Yacine');
      expect(mongObject.lastName).toBe('KHATAL');
      expect(mongObject.age).toBe(25);
      expect(mongObject.job).toBeUndefined();
      expect(mongObject.company).toBeUndefined();
      expect(mongObject._version).toBe(8);

      done();
    });
  });
});
<|endoftext|>


<commit_before>define([
	'modules/MainMenu/src/MainMenuModel',
	'modules/MainMenu/src/MainMenuController',
	'modules/MainMenu/src/MainMenuView'
], function(MainMenuModel, MainMenuController, MainMenuView) {
	return {
		run: function() {
			test("MainMenuController", function(assert) {
				var mmc = new MainMenuController();
				assert.ok(mmc instanceof MainMenuController);

				mmc.model.addMenu({title:'Notes', view: undefined});
				mmc.model.addMenu({title:'Chords', view: undefined});

				mmc.activeMenu('Lyrics');
				assert.equal(mmc.model.getCurrentMenu().title, 'Notes', "Try active menu on a menu that doesn't exist. Default is the first Menu.");

				mmc.activeMenu('Chords');
				assert.equal(mmc.model.getCurrentMenu().title, 'Chords', "try to activate a menu");
			});
		}
	};
});<commit_msg>removed obsolete menu tests<commit_after>define([
	'modules/MainMenu/src/MainMenuModel',
	'modules/MainMenu/src/MainMenuController',
	'modules/MainMenu/src/MainMenuView'
], function(MainMenuModel, MainMenuController, MainMenuView) {
	return {
		run: function() {
			test("MainMenuController", function(assert) {
				var mmc = new MainMenuController();
				assert.ok(mmc instanceof MainMenuController);
				mmc.model.addMenu({title:'Notes', view: undefined});
				mmc.model.addMenu({title:'Chords', view: undefined});

			});
		}
	};
});<|endoftext|>


<commit_before>"use strict";

treeherder.controller('JobsCtrl',
    function JobsCtrl($scope, $http, $rootScope, $routeParams, ThLog, $cookies,
                      localStorageService, thUrl, ThRepositoryModel, thSocket,
                      ThResultSetModel, thResultStatusList) {
        var $log = new ThLog(this.constructor.name);

        // load our initial set of resultsets
        // scope needs this function so it can be called directly by the user, too.
        $scope.fetchResultSets = function(count) {
            ThResultSetModel.fetchResultSets($scope.repoName, count);
        };

        // set the default repo to mozilla-inbound if not specified
        if ($routeParams.hasOwnProperty("repo") &&
            $routeParams.repo !== "") {
            $rootScope.repoName = $routeParams.repo;
        } else {
            $rootScope.repoName = "mozilla-inbound";
        }

        // load the list of repos into $rootScope, and set the current repo.
        ThRepositoryModel.load($scope.repoName);

        ThResultSetModel.addRepository($scope.repoName);

        $scope.isLoadingRsBatch = ThResultSetModel.getLoadingStatus($scope.repoName);
        $scope.result_sets = ThResultSetModel.getResultSetsArray($scope.repoName);
        $scope.job_map = ThResultSetModel.getJobMap($scope.repoName);
        $scope.statusList = thResultStatusList;

        if(ThResultSetModel.isNotLoaded($scope.repoName)){
            // get our first set of resultsets
            ThResultSetModel.fetchResultSets($scope.repoName, 10);
        }

    }
);

treeherder.controller('ResultSetCtrl',
    function ResultSetCtrl($scope, $rootScope, $http, ThLog, $location,
                           thUrl, thServiceDomain, thResultStatusInfo,
                           ThResultSetModel, thEvents, thJobFilters) {

        var $log = new ThLog(this.constructor.name);

        $scope.getCountClass = function(resultStatus) {
            return thResultStatusInfo(resultStatus).btnClass;
        };
        $scope.getCountText = function(resultStatus) {
            return thResultStatusInfo(resultStatus).countText;
        };
        $scope.viewJob = function(job) {
            // set the selected job
            $rootScope.selectedJob = job;
        };
        $scope.viewLog = function(job_uri) {
            // open the logviewer for this job in a new window
            // currently, invoked by right-clicking a job.

            $http.get(thServiceDomain + job_uri).
                success(function(data) {
                    if (data.hasOwnProperty("artifacts")) {
                        data.artifacts.forEach(function(artifact) {
                            if (artifact.name === "Structured Log") {
                                window.open(thUrl.getLogViewerUrl(artifact.id));
                            }
                        });
                    } else {
                        $log.warn("Job had no artifacts: " + job_uri);
                    }
                });

        };

        $scope.toggleRevisions = function() {

            ThResultSetModel.loadRevisions(
                $rootScope.repoName, $scope.resultset.id
                );

            $rootScope.$broadcast(
                thEvents.toggleRevisions, $scope.resultset
                );

        };
        $scope.toggleJobs = function() {

            $rootScope.$broadcast(
                thEvents.toggleJobs, $scope.resultset
                );

        };

        var RevisionListModalInstanceCtrl = function ($scope, $modalInstance, items) {
            $scope.revisions = items;
        };

        /**
         * When the user clicks one of the resultStates on the resultset line,
         * then toggle what is showing for just this resultset.
         *
         * @param resultStatus - Which resultStatus to toggle.
         */
        $scope.toggleResultSetResultStatusFilter = function(resultStatus) {
            var idx = $scope.resultStatusFilters.indexOf(resultStatus);
            if (idx < 0) {
                $scope.resultStatusFilters.push(resultStatus);
            } else {
                $scope.resultStatusFilters.splice(idx, 1);
            }

            $rootScope.$broadcast(
                thEvents.resultSetFilterChanged, $scope.resultset
                );

            $log.debug("toggled: ", resultStatus);
            $log.debug("resultStatusFilters", $scope.resultStatusFilters);
        };

        /**
         * Whether or not a job should be shown based on the global and local
         * filters.
         * @param job
         */
        $scope.showJob = function(job) {
            return thJobFilters.showJob(job, $scope.resultStatusFilters);
        };

        $scope.revisionResultsetFilterUrl = $scope.urlBasePath + "?repo=" + $scope.repoName + "&revision=" + $scope.resultset.revision;
        $scope.authorResultsetFilterUrl = $scope.urlBasePath + "?repo=" + $scope.repoName + "&author=" + encodeURIComponent($scope.resultset.author);

        $scope.resultStatusFilters = thJobFilters.copyResultStatusFilters();

        $scope.isCollapsedResults = false;

        // whether or not revision list for a resultset is collapsed
        $scope.isCollapsedRevisions = true;

        $rootScope.$on(thEvents.jobContextMenu, function(event, job){
            $log.debug("caught", thEvents.jobContextMenu);
            //$scope.viewLog(job.resource_uri);
        });
    }
);

<commit_msg>remove dead code<commit_after>"use strict";

treeherder.controller('JobsCtrl',
    function JobsCtrl($scope, $http, $rootScope, $routeParams, ThLog, $cookies,
                      localStorageService, thUrl, ThRepositoryModel, thSocket,
                      ThResultSetModel, thResultStatusList) {
        var $log = new ThLog(this.constructor.name);

        // load our initial set of resultsets
        // scope needs this function so it can be called directly by the user, too.
        $scope.fetchResultSets = function(count) {
            ThResultSetModel.fetchResultSets($scope.repoName, count);
        };

        // set the default repo to mozilla-inbound if not specified
        if ($routeParams.hasOwnProperty("repo") &&
            $routeParams.repo !== "") {
            $rootScope.repoName = $routeParams.repo;
        } else {
            $rootScope.repoName = "mozilla-inbound";
        }

        // load the list of repos into $rootScope, and set the current repo.
        ThRepositoryModel.load($scope.repoName);

        ThResultSetModel.addRepository($scope.repoName);

        $scope.isLoadingRsBatch = ThResultSetModel.getLoadingStatus($scope.repoName);
        $scope.result_sets = ThResultSetModel.getResultSetsArray($scope.repoName);
        $scope.job_map = ThResultSetModel.getJobMap($scope.repoName);
        $scope.statusList = thResultStatusList;

        if(ThResultSetModel.isNotLoaded($scope.repoName)){
            // get our first set of resultsets
            ThResultSetModel.fetchResultSets($scope.repoName, 10);
        }

    }
);

treeherder.controller('ResultSetCtrl',
    function ResultSetCtrl($scope, $rootScope, $http, ThLog, $location,
                           thUrl, thServiceDomain, thResultStatusInfo,
                           ThResultSetModel, thEvents, thJobFilters) {

        var $log = new ThLog(this.constructor.name);

        $scope.getCountClass = function(resultStatus) {
            return thResultStatusInfo(resultStatus).btnClass;
        };
        $scope.getCountText = function(resultStatus) {
            return thResultStatusInfo(resultStatus).countText;
        };
        $scope.viewJob = function(job) {
            // set the selected job
            $rootScope.selectedJob = job;
        };
        $scope.viewLog = function(job_uri) {
            // open the logviewer for this job in a new window
            // currently, invoked by right-clicking a job.

            $http.get(thServiceDomain + job_uri).
                success(function(data) {
                    if (data.hasOwnProperty("artifacts")) {
                        data.artifacts.forEach(function(artifact) {
                            if (artifact.name === "Structured Log") {
                                window.open(thUrl.getLogViewerUrl(artifact.id));
                            }
                        });
                    } else {
                        $log.warn("Job had no artifacts: " + job_uri);
                    }
                });

        };

        $scope.toggleRevisions = function() {

            ThResultSetModel.loadRevisions(
                $rootScope.repoName, $scope.resultset.id
                );

            $rootScope.$broadcast(
                thEvents.toggleRevisions, $scope.resultset
                );

        };
        $scope.toggleJobs = function() {

            $rootScope.$broadcast(
                thEvents.toggleJobs, $scope.resultset
                );

        };

        /**
         * When the user clicks one of the resultStates on the resultset line,
         * then toggle what is showing for just this resultset.
         *
         * @param resultStatus - Which resultStatus to toggle.
         */
        $scope.toggleResultSetResultStatusFilter = function(resultStatus) {
            var idx = $scope.resultStatusFilters.indexOf(resultStatus);
            if (idx < 0) {
                $scope.resultStatusFilters.push(resultStatus);
            } else {
                $scope.resultStatusFilters.splice(idx, 1);
            }

            $rootScope.$broadcast(
                thEvents.resultSetFilterChanged, $scope.resultset
                );

            $log.debug("toggled: ", resultStatus);
            $log.debug("resultStatusFilters", $scope.resultStatusFilters);
        };

        /**
         * Whether or not a job should be shown based on the global and local
         * filters.
         * @param job
         */
        $scope.showJob = function(job) {
            return thJobFilters.showJob(job, $scope.resultStatusFilters);
        };

        $scope.revisionResultsetFilterUrl = $scope.urlBasePath + "?repo=" + $scope.repoName + "&revision=" + $scope.resultset.revision;
        $scope.authorResultsetFilterUrl = $scope.urlBasePath + "?repo=" + $scope.repoName + "&author=" + encodeURIComponent($scope.resultset.author);

        $scope.resultStatusFilters = thJobFilters.copyResultStatusFilters();

        $scope.isCollapsedResults = false;

        // whether or not revision list for a resultset is collapsed
        $scope.isCollapsedRevisions = true;

        $rootScope.$on(thEvents.jobContextMenu, function(event, job){
            $log.debug("caught", thEvents.jobContextMenu);
            //$scope.viewLog(job.resource_uri);
        });
    }
);

<|endoftext|>


<commit_before>'use strict';

function RequestTypeError(requestType) {
	this.name = 'RequestTypeError';
	this.message = requestType ? 'No onRequest handler for type "' + requestType + '"' : 'No handler defined for request';
}

RequestTypeError.prototype = Object.create(Error.prototype);
RequestTypeError.prototype.constructor = RequestTypeError;

module.exports = RequestTypeError;
<commit_msg>update custom request type error to use inherits<commit_after>'use strict';
var inherits = require('inherits');

function RequestTypeError(requestType) {
	this.name = 'RequestTypeError';
	this.message = requestType ? 'No onRequest handler for type "' + requestType + '"' : 'No handler defined for request';
}
inherits(RequestTypeError, Error);

module.exports = RequestTypeError;
<|endoftext|>


<commit_before>import pathToRegexp from 'path-to-regexp'
import {rest, viewPersistor} from 'tocco-app-extensions'
import _get from 'lodash/get'
import _pickBy from 'lodash/pickBy'
import _pick from 'lodash/pick'
import {takeLatest, fork, all, call, put, select, spawn} from 'redux-saga/effects'

import * as actions from './actions'

export const modelSelector = (state, entity) => _get(state, `entities.path.modelCache.${entity}`, null)
export const displaySelector = (state, entity, key) => _get(state, `entities.path.displayCache.${entity}.${key}`, null)

export const breadcrumbsSelector = state => state.entities.path.breadcrumbsInfo
export const currentViewInfosSelector = state => state.entities.path.currentViewInfos

const isEven = n => n % 2 === 0

export function* getModel(entity) {
  const cachedModel = yield select(modelSelector, entity)
  if (cachedModel) {
    return cachedModel
  }

  const entityModel = yield call(rest.fetchModel, entity)
  yield put(actions.cacheModel(entity, entityModel))
  return entityModel
}

export function* extractMultiRelations(model, key) {
  if (key !== null) {
    const relations = _pickBy(model.paths, (value, key) => value.relationDisplay && value.relationDisplay.show)

    const relationsTransformed = Object.keys(relations)
      .map(k =>
        _pick(relations[k],
          ['relationName', 'reverseRelationName', 'targetEntity', 'relationDisplay.label', 'relationDisplay.order']
        )
      )
      .sort((a, b) => a.relationDisplay.order > b.relationDisplay.order ? 1 : -1)

    yield put(actions.setRelations(relationsTransformed))
    yield spawn(loadRelationCounts, relationsTransformed, key)
  }
}

export function* getDisplay(entityName, key, breadcrumbsIdx) {
  let display = yield select(displaySelector, entityName, key)
  if (!display) {
    display = yield call(rest.fetchDisplay, entityName, key)

    yield put(actions.cacheDisplay(entityName, key, display))
  }

  yield put(actions.setBreadcrumbDisplay(display, breadcrumbsIdx))
}

export function* addEntityToBreadcrumbs(path, entityName) {
  const breadcrumbs = yield select(breadcrumbsSelector)
  const type = 'list'
  const display = entityName

  yield put(actions.setBreadcrumbsInfo([...breadcrumbs, {display, path, type}]))
}

export function* loadRelationCounts(relations, key) {
  const a = yield all(relations.map(r => call(rest.fetchEntityCount, r.targetEntity, {
    conditions: {
      [r.reverseRelationName + '.pk']: {value: key}
    }
  })))

  const relationsCount = a.reduce((accumulator, currentValue, idx) => {
    return {...accumulator, [relations[idx].relationName]: currentValue}
  }, {})
  yield put(actions.setRelationCount(relationsCount))
}

export function* addRecordToBreadcrumbs(path, entityName, key) {
  const type = 'record'
  const breadcrumbs = yield select(breadcrumbsSelector)
  const display = '...'
  yield put(actions.setBreadcrumbsInfo([...breadcrumbs, {display, path, type}]))

  if (type === 'record') {
    yield spawn(getDisplay, entityName, key, breadcrumbs.length)
  }
}

export function* loadCurrentViewInfo({payload: {location}}) {
  const currentViewInfo = {
    model: null,
    key: null,
    reverseRelation: null,
    parentModel: null,
    relations: null,
    location,
    level: 0
  }

  yield put(actions.setBreadcrumbsInfo([]))

  const pathParts = []
  const re = pathToRegexp('/e/:entity/:key?/:relation*/:view(list|detail|edit|create|relations)', pathParts)
  const res = re.exec(location)

  if (res !== null) {
    const path = {}
    pathParts.forEach((key, idx) => {
      path[key.name] = res[idx + 1]
    })

    if (path.entity) {
      const baseEntityModel = yield call(getModel, path.entity)
      yield call(addEntityToBreadcrumbs, path.entity, baseEntityModel.label)
      currentViewInfo.model = baseEntityModel
      currentViewInfo.level = 0

      if (path.key) {
        currentViewInfo.key = path.key
        currentViewInfo.level = 1

        yield call(addRecordToBreadcrumbs, path.entity + '/' + path.key, path.entity, path.key)

        if (path.relation) {
          const splittedRelation = path.relation.split('/')
          for (let i = 0; i < splittedRelation.length; i++) {
            const relationStringPart = splittedRelation[i]
            const breadcrumbPath = [path.entity, path.key, ...path.relation.split('/').slice(0, i + 1)].join('/')

            currentViewInfo.level = i + 2

            if (isEven(i)) {
              const relationName = relationStringPart
              const targetEntity = currentViewInfo.model.paths[relationName].targetEntity
              currentViewInfo.reverseRelation = currentViewInfo.model.paths[relationName].reverseRelationName
              currentViewInfo.parentModel = currentViewInfo.model
              currentViewInfo.model = yield call(getModel, targetEntity)

              yield call(addEntityToBreadcrumbs, breadcrumbPath, currentViewInfo.model.name)
            } else {
              const key = relationStringPart
              currentViewInfo.key = key
              yield call(addRecordToBreadcrumbs, breadcrumbPath, currentViewInfo.model.name, key)
            }
          }
        }
      }

      yield spawn(extractMultiRelations, currentViewInfo.model, currentViewInfo.key)

      const currentViewInfos = yield select(currentViewInfosSelector)

      if (!currentViewInfos[location]) {
        yield put(actions.setCurrentViewInfo(location, currentViewInfo))
      }

      yield put(viewPersistor.clearPersistedViews(currentViewInfo.level + 1))
    }
  }
}

export default function* mainSagas() {
  yield all([
    fork(takeLatest, actions.LOAD_CURRENT_VIEW_INFO, loadCurrentViewInfo)
  ])
}
<commit_msg>fix(admin): translation in breadcrumbs<commit_after>import pathToRegexp from 'path-to-regexp'
import {rest, viewPersistor} from 'tocco-app-extensions'
import _get from 'lodash/get'
import _pickBy from 'lodash/pickBy'
import _pick from 'lodash/pick'
import {takeLatest, fork, all, call, put, select, spawn} from 'redux-saga/effects'

import * as actions from './actions'

export const modelSelector = (state, entity) => _get(state, `entities.path.modelCache.${entity}`, null)
export const displaySelector = (state, entity, key) => _get(state, `entities.path.displayCache.${entity}.${key}`, null)

export const breadcrumbsSelector = state => state.entities.path.breadcrumbsInfo
export const currentViewInfosSelector = state => state.entities.path.currentViewInfos

const isEven = n => n % 2 === 0

export function* getModel(entity) {
  const cachedModel = yield select(modelSelector, entity)
  if (cachedModel) {
    return cachedModel
  }

  const entityModel = yield call(rest.fetchModel, entity)
  yield put(actions.cacheModel(entity, entityModel))
  return entityModel
}

export function* extractMultiRelations(model, key) {
  if (key !== null) {
    const relations = _pickBy(model.paths, (value, key) => value.relationDisplay && value.relationDisplay.show)

    const relationsTransformed = Object.keys(relations)
      .map(k =>
        _pick(relations[k],
          ['relationName', 'reverseRelationName', 'targetEntity', 'relationDisplay.label', 'relationDisplay.order']
        )
      )
      .sort((a, b) => a.relationDisplay.order > b.relationDisplay.order ? 1 : -1)

    yield put(actions.setRelations(relationsTransformed))
    yield spawn(loadRelationCounts, relationsTransformed, key)
  }
}

export function* getDisplay(entityName, key, breadcrumbsIdx) {
  let display = yield select(displaySelector, entityName, key)
  if (!display) {
    display = yield call(rest.fetchDisplay, entityName, key)

    yield put(actions.cacheDisplay(entityName, key, display))
  }

  yield put(actions.setBreadcrumbDisplay(display, breadcrumbsIdx))
}

export function* addEntityToBreadcrumbs(path, entityName) {
  const breadcrumbs = yield select(breadcrumbsSelector)
  const type = 'list'
  const display = entityName

  yield put(actions.setBreadcrumbsInfo([...breadcrumbs, {display, path, type}]))
}

export function* loadRelationCounts(relations, key) {
  const a = yield all(relations.map(r => call(rest.fetchEntityCount, r.targetEntity, {
    conditions: {
      [r.reverseRelationName + '.pk']: {value: key}
    }
  })))

  const relationsCount = a.reduce((accumulator, currentValue, idx) => {
    return {...accumulator, [relations[idx].relationName]: currentValue}
  }, {})
  yield put(actions.setRelationCount(relationsCount))
}

export function* addRecordToBreadcrumbs(path, entityName, key) {
  const type = 'record'
  const breadcrumbs = yield select(breadcrumbsSelector)
  const display = '...'
  yield put(actions.setBreadcrumbsInfo([...breadcrumbs, {display, path, type}]))

  if (type === 'record') {
    yield spawn(getDisplay, entityName, key, breadcrumbs.length)
  }
}

export function* loadCurrentViewInfo({payload: {location}}) {
  const currentViewInfo = {
    model: null,
    key: null,
    reverseRelation: null,
    parentModel: null,
    relations: null,
    location,
    level: 0
  }

  yield put(actions.setBreadcrumbsInfo([]))

  const pathParts = []
  const re = pathToRegexp('/e/:entity/:key?/:relation*/:view(list|detail|edit|create|relations)', pathParts)
  const res = re.exec(location)

  if (res !== null) {
    const path = {}
    pathParts.forEach((key, idx) => {
      path[key.name] = res[idx + 1]
    })

    if (path.entity) {
      const baseEntityModel = yield call(getModel, path.entity)
      yield call(addEntityToBreadcrumbs, path.entity, baseEntityModel.label)
      currentViewInfo.model = baseEntityModel
      currentViewInfo.level = 0

      if (path.key) {
        currentViewInfo.key = path.key
        currentViewInfo.level = 1

        yield call(addRecordToBreadcrumbs, path.entity + '/' + path.key, path.entity, path.key)

        if (path.relation) {
          const splittedRelation = path.relation.split('/')
          for (let i = 0; i < splittedRelation.length; i++) {
            const relationStringPart = splittedRelation[i]
            const breadcrumbPath = [path.entity, path.key, ...path.relation.split('/').slice(0, i + 1)].join('/')

            currentViewInfo.level = i + 2

            if (isEven(i)) {
              const relationName = relationStringPart
              const targetEntity = currentViewInfo.model.paths[relationName].targetEntity
              currentViewInfo.reverseRelation = currentViewInfo.model.paths[relationName].reverseRelationName
              currentViewInfo.parentModel = currentViewInfo.model
              currentViewInfo.model = yield call(getModel, targetEntity)

              yield call(addEntityToBreadcrumbs, breadcrumbPath, currentViewInfo.model.label)
            } else {
              const key = relationStringPart
              currentViewInfo.key = key
              yield call(addRecordToBreadcrumbs, breadcrumbPath, currentViewInfo.model.name, key)
            }
          }
        }
      }

      yield spawn(extractMultiRelations, currentViewInfo.model, currentViewInfo.key)

      const currentViewInfos = yield select(currentViewInfosSelector)

      if (!currentViewInfos[location]) {
        yield put(actions.setCurrentViewInfo(location, currentViewInfo))
      }

      yield put(viewPersistor.clearPersistedViews(currentViewInfo.level + 1))
    }
  }
}

export default function* mainSagas() {
  yield all([
    fork(takeLatest, actions.LOAD_CURRENT_VIEW_INFO, loadCurrentViewInfo)
  ])
}
<|endoftext|>


<commit_before>import PricingIndexRoute from 'hospitalrun/pricing/index/route';
export default PricingIndexRoute.extend({
    category: 'Ward',
    pageTitle: 'Ward Pricing',

    actions: {
        editItem: function(item) {
            item.set('returnTo','pricing.imaging');
            this.transitionTo('pricing.edit', item);
        }
    }
});<commit_msg>Fixed return from ward pricing<commit_after>import PricingIndexRoute from 'hospitalrun/pricing/index/route';
export default PricingIndexRoute.extend({
    category: 'Ward',
    pageTitle: 'Ward Pricing',

    actions: {
        editItem: function(item) {
            item.set('returnTo','pricing.ward');
            this.transitionTo('pricing.edit', item);
        }
    }
});<|endoftext|>


<commit_before>var path = require('path')

module.exports = {
  devtool: '#eval-source-map',
  entry: {
    app: ['./src/index.js']
  },
  output: {
    path: path.resolve(__dirname, 'public/assets'),
    publicPath: 'assets/',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel']
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      },
      {
        test: /\.scss$/,
        loaders: ['style', 'css', 'sass']
      }
    ]
  }
}
<commit_msg>Use array of loaders<commit_after>var path = require('path')

module.exports = {
  devtool: '#eval-source-map',
  entry: {
    app: ['./src/index.js']
  },
  output: {
    path: path.resolve(__dirname, 'public/assets'),
    publicPath: 'assets/',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel']
      },
      {
        test: /\.css$/,
        loaders: ['style', 'css']
      },
      {
        test: /\.scss$/,
        loaders: ['style', 'css', 'sass']
      }
    ]
  }
}
<|endoftext|>


<commit_before>whenReady(function() {

  if (Covered.page.current_user) {
    var user = Covered.page.current_user;
    var project = Covered.page.current_project || {};
    // Identify the user and pass traits
    // To enable, replace sample data with actual user traits and uncomment the line
    UserVoice.push(['identify', {
      email:      user.email_address, // User’s email address
      name:       user.name, // User’s real name
      id:         user.id, // Optional: Unique id of the user (if set, this should not change)

      //created_at: 1364406966, // Unix timestamp for the date the user signed up
      //type:       'Owner', // Optional: segment your users by type
      account: {
        id:           project.id, // Optional: associate multiple users with a single account
        name:         project.name, // Account name
        // created_at:   1364406966, // Unix timestamp for the date the account was created
        // monthly_rate: 9.99, // Decimal; monthly rate of the account
        // ltv:          1495.00, // Decimal; lifetime value of the account
        // plan:         'Enhanced' // Plan name for the account
      }
    }]);

    // Add default trigger to the bottom-right corner of the window:
    UserVoice.push(['addTrigger', { mode: 'contact', trigger_style: 'tab', trigger_position: 'left' }]);
  }

});

<commit_msg>moving uservoice to the right side<commit_after>whenReady(function() {

  if (Covered.page.current_user) {
    var user = Covered.page.current_user;
    var project = Covered.page.current_project || {};
    // Identify the user and pass traits
    // To enable, replace sample data with actual user traits and uncomment the line
    UserVoice.push(['identify', {
      email:      user.email_address, // User’s email address
      name:       user.name, // User’s real name
      id:         user.id, // Optional: Unique id of the user (if set, this should not change)

      //created_at: 1364406966, // Unix timestamp for the date the user signed up
      //type:       'Owner', // Optional: segment your users by type
      account: {
        id:           project.id, // Optional: associate multiple users with a single account
        name:         project.name, // Account name
        // created_at:   1364406966, // Unix timestamp for the date the account was created
        // monthly_rate: 9.99, // Decimal; monthly rate of the account
        // ltv:          1495.00, // Decimal; lifetime value of the account
        // plan:         'Enhanced' // Plan name for the account
      }
    }]);

    // Add default trigger to the bottom-right corner of the window:
    UserVoice.push(['addTrigger', { mode: 'contact', trigger_style: 'tab', trigger_position: 'right' }]);
  }

});

<|endoftext|>