<commit_before>((function (App) {
  'use strict';

  App.View.ChartWidgetView = Backbone.View.extend({

    template: HandlebarsTemplates['front/chart-widget'],

    defaults: {
      // Ratio between the height and the width (i.e. height = chartRatio * width)
      // If you update this value, please update the .large-widget's height (l-analysis-dashboard)
      chartRatio: 0.5,
      // Data to display on the chart
      data: [],
      // Name of the default chart type
      chart: null,
      // Name of the column x
      columnX: null,
      // Name of the column y
      columnY: null,
      // Enable the chart selector
      enableChartSelector: true,
      // Callback to execute when the switch button is pressed
      // The switch button will only appear if switchCallback is a function
      // The swith button let the user switch the chart for a map
      switchCallback: null,
      // Inner width of the chart, used internally
      _width: null,
      // Inner height of the chart, used internally
      _height: null
    },

    initialize: function (settings) {
      this.options = Object.assign({}, this.defaults, settings);
      if (this.options.data.length) this.widgetToolbox = new App.Helper.WidgetToolbox(this.options.data);
      this._setListeners();

      // We pre-render the component with its template
      this.el.innerHTML = this.template();
      this.chartContainer = this.el.querySelector('.js-chart');
      this.chartSelectorContainer = this.el.querySelector('.js-chart-selector');
    },

    /**
     * Set the listeners that doesn't depend on a DOM node
     */
    _setListeners: function () {
      window.addEventListener('resize', _.debounce(this._onResize.bind(this), 150));
    },

    /**
     * Event handler for when the window is resized
     */
    _onResize: function () {
      if (!this.options.chart) return;

      /* eslint-disable no-underscore-dangle */
      var previousWidth = this.options._width;
      var previousHeight = this.options._height;
      /* eslint-enable no-underscore-dangle */

      var newDimensions = this._computeChartDimensions();
      if (newDimensions.width !== previousWidth || newDimensions.height !== previousHeight) {
        this._renderChart();
      }
    },

    /**
     * Event handler for when the chart is changed by the selector
     * @param {string[]} - chart, column x, column y
     */
    _onChangeChart: function () {
      this.options.chart = arguments[0][0];
      this.options.columnX = arguments[0][1];
      this.options.columnY = arguments[0].length > 2 ? arguments[0][2] : null;
      this._renderChart();
    },

    /**
     * Compute the chart dimensions
     * @returns {{ width: number, height: number}}
     */
    _computeChartDimensions: function () {
      // We render the template with fake data in order to parse it as a JS object and retrieve the padding
      var chartTemplate = JSON.parse(this._getChartTemplate()({
        data: JSON.stringify([]),
        xColumn: JSON.stringify(''),
        yColumn: JSON.stringify(''),
        xLabel: JSON.stringify(''),
        yLabel: JSON.stringify(''),
        width: JSON.stringify(''),
        height: JSON.stringify('')
      }));

      var containerDimensions = this.chartContainer.getBoundingClientRect();

      var padding = Object.assign({}, chartTemplate.padding, {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      });

      var width = Math.round(containerDimensions.width - padding.left - padding.right);
      var height = Math.round(width * this.options.chartRatio);

      // We save the current dimensions of the chart to diff them whenever the window is resized in order to minimize
      // the number of re-renders
      /* eslint-disable no-underscore-dangle */
      this.options._width = width;
      this.options._height = height;
      /* eslint-enable no-underscore-dangle */

      return {
        width: width,
        height: height
      };
    },

    /**
     * Get the chart Handlebars template
     * @returns {function}
     */
    _getChartTemplate: function () {
      if (!this.options.data.length) return HandlebarsTemplates['front/charts/empty'];
      return HandlebarsTemplates['front/charts/' + this.options.chart];
    },

    /**
     * Generate the vega spec
     * @returns {object}
     */
    _generateVegaSpec: function () {
      if (this.options.data.length && !this.options.chart) {
        var availableCharts = this.widgetToolbox.getAvailableCharts();
        if (availableCharts.length) {
          this.options.chart = availableCharts[0];
        } else {
          // eslint-disable-next-line no-console
          console.warn('Unable to generate a chart out of the current dataset');
          return {};
        }
      }

      var chartDimensions = this._computeChartDimensions();

      // We check if we need to automatically select the columns
      if (this.options.data.length && !this.options.columnX && !this.options.columnY) {
        var columns = this.widgetToolbox.getChartRandomColumns(this.options.chart);
        this.options.columnX = columns.x;
        this.options.columnY = columns.y;
      }
      const columnX = JSON.stringify(this.options.columnX);
      const columnY = JSON.stringify(this.options.columnY);
      const labelX = this.options.xLabel;
      const labelY = this.options.yLabel;
      var xLabel = columnX;
      var yLabel = columnY;

      if (labelX !== undefined && labelX !== null && labelX !== ''){
        xLabel = JSON.stringify(labelX);}

      if (labelY !== undefined && labelY !== null && labelY !== ''){
        yLabel = JSON.stringify(labelY);}

      return this._getChartTemplate()({
        data: JSON.stringify(this.options.data),
        xColumn: columnX,
        yColumn: columnY,
        width: chartDimensions.width,
        height: chartDimensions.height,
        xLabel: xLabel,
        yLabel: yLabel
      });
    },

    /**
     * Create the chart and append it to the DOM
     */
    _renderChart: function () {
      // The widget toolbox could be non-assigned if the view has been instantiated
      // with no data
      if (this.options.data.length && !this.widgetToolbox) {
        this.widgetToolbox = new App.Helper.WidgetToolbox(this.options.data);
      }

      vg.parse
        .spec(JSON.parse(this._generateVegaSpec()), function (error, chart) {
          if (error) {
            App.notifications.broadcast(App.Helper.Notifications.dashboard.chartError)
            return;
          }
          this.chart = chart({
            el: this.chartContainer,
            // By using the SVG renderer, we give the client the posibility to
            // translate the text contained in the charts
            renderer: 'svg'
          }).update();
        }.bind(this));

      // We don't want to trigger anything if the dataset is empty
      if (!this.options.data.length) return;

      // We save the state of the widget each time we render as it can be the
      // consequence of a change in the configuration
      // NOTE: We need to make sure in case the view hasn't been instanciated with
      // a chart configuration that it's set by the toolbox before triggering
      this.trigger('state:change', {
        type: 'chart',
        chart: this.options.chart,
        x: this.options.columnX,
        y: this.options.columnY,
        xLabel: this.options.xLabel,
        yLabel: this.options.yLabel
      });
    },

    /**
     * Create the chart selector and append it to the DOM
     */
    _renderChartSelector: function () {
      var hierarchy = { label: 'Customize chart', options: [] };
      hierarchy.options = this.widgetToolbox.getAvailableCharts().map(function (chartName) {
        var chart = _.findWhere(this.widgetToolbox.getChartConfig(), { name: chartName });
        var acceptedColumnsNb = chart.acceptedStatTypes[0].length;

        var res = { name: chartName, id: chartName };
        res.label = acceptedColumnsNb === 1 ? 'Select a column' : 'Select X column';
        res.options = this.widgetToolbox.getAvailableXColumns(chartName).map(function (xColumn) {
          var o = {};
          o.name = xColumn;
          o.id = xColumn;

          if (acceptedColumnsNb > 1) {
            o.label = 'Select Y column';
            o.options = this.widgetToolbox.getAvailableYColumns(chartName, xColumn).map(function (yColumn) {
              // eslint-disable-next-line no-shadow
              var o = {};
              o.name = yColumn;
              o.id = yColumn;
              return o;
            });
          }

          return o;
        }, this);

        return res;
      }, this);

      new App.View.HierarchicalSelectView({
        el: this.chartSelectorContainer,
        ID: +(new Date()),
        hierarchy: hierarchy,
        onChange: this._onChangeChart.bind(this)
      });
    },

    /**
     * Render the switch button and attach an event listener to it
     */
    _renderSwitchButton: function () {
      // We create the button
      var button = document.createElement('button');
      button.type = 'button';
      button.classList.add('c-button', 'switch-button');
      button.textContent = 'Switch for map';

      // We attach the listener
      button.addEventListener('click', this.options.switchCallback);

      // We append the button to the DOM
      this.el.querySelector('.js-switch-button').appendChild(button);
    },

    /**
     * Render the Custom Axis Label Inputs and attach an event listener to it
     */
    _renderCustomAxisLabelInput: function () {
      const axis = ['X','Y'];

      axis.forEach(function(axis){
        // We create the input
        var input = document.createElement('input');
        input.setAttribute('placeholder', 'Custom ' + axis + ' label');
        input.setAttribute('name', 'custom-' + axis);
        const axisLabel = (axis === 'X') ? this.options.xLabel : this.options.yLabel;
        if (axisLabel !== undefined) { input.value = axisLabel };

        // We attach the listeners
        input.addEventListener('change', function(){
          this._changeAxisLabel(axis , input.value);
        }.bind(this));

        // We append the inputs to the DOM
        this.el.querySelector('#custom-axis-input-container').appendChild(input);

      }.bind(this))
    },
    _changeAxisLabel: function(axis, value){
      if(axis === 'X') this.options.xLabel = value;
      else this.options.yLabel = value;
      this._renderChart();
    },
    /**
     * Remove the changes the component implied to the container and all of
     * its children
     */
    remove: function () {
      this.el.innerHTML = '';
    },

    render: function () {
      this._renderChart();

      // We don't render the chart selector and the switch button if the dataset is empty
      if (this.options.data.length) {
        if (this.options.enableChartSelector){
          this._renderChartSelector();
          // TODO: use displayMode to enable the render of rest of the buttons and inputs
          if(this.options.displayMode !== 'dashboard'){
            this._renderCustomAxisLabelInput();
          }
        }
        if (this.options.switchCallback && typeof this.options.switchCallback === 'function') {
          this._renderSwitchButton();
        }
      }

      return this.el;
    }

  });
})(this.App));
<commit_msg>when the chart is altered the custom labels are set to null<commit_after>((function (App) {
  'use strict';

  App.View.ChartWidgetView = Backbone.View.extend({

    template: HandlebarsTemplates['front/chart-widget'],

    defaults: {
      // Ratio between the height and the width (i.e. height = chartRatio * width)
      // If you update this value, please update the .large-widget's height (l-analysis-dashboard)
      chartRatio: 0.5,
      // Data to display on the chart
      data: [],
      // Name of the default chart type
      chart: null,
      // Name of the column x
      columnX: null,
      // Name of the column y
      columnY: null,
      // Enable the chart selector
      enableChartSelector: true,
      // Callback to execute when the switch button is pressed
      // The switch button will only appear if switchCallback is a function
      // The swith button let the user switch the chart for a map
      switchCallback: null,
      // Inner width of the chart, used internally
      _width: null,
      // Inner height of the chart, used internally
      _height: null
    },

    initialize: function (settings) {
      this.options = Object.assign({}, this.defaults, settings);
      if (this.options.data.length) this.widgetToolbox = new App.Helper.WidgetToolbox(this.options.data);
      this._setListeners();

      // We pre-render the component with its template
      this.el.innerHTML = this.template();
      this.chartContainer = this.el.querySelector('.js-chart');
      this.chartSelectorContainer = this.el.querySelector('.js-chart-selector');
    },

    /**
     * Set the listeners that doesn't depend on a DOM node
     */
    _setListeners: function () {
      window.addEventListener('resize', _.debounce(this._onResize.bind(this), 150));
    },

    /**
     * Event handler for when the window is resized
     */
    _onResize: function () {
      if (!this.options.chart) return;

      /* eslint-disable no-underscore-dangle */
      var previousWidth = this.options._width;
      var previousHeight = this.options._height;
      /* eslint-enable no-underscore-dangle */

      var newDimensions = this._computeChartDimensions();
      if (newDimensions.width !== previousWidth || newDimensions.height !== previousHeight) {
        this._renderChart();
      }
    },

    /**
     * Event handler for when the chart is changed by the selector
     * @param {string[]} - chart, column x, column y
     */
    _onChangeChart: function () {
      this.options.chart = arguments[0][0];
      this.options.columnX = arguments[0][1];
      this.options.columnY = arguments[0].length > 2 ? arguments[0][2] : null;
      this.options.xLabel = null;
      this.options.yLabel = null;
      this._renderChart();
    },

    /**
     * Compute the chart dimensions
     * @returns {{ width: number, height: number}}
     */
    _computeChartDimensions: function () {
      // We render the template with fake data in order to parse it as a JS object and retrieve the padding
      var chartTemplate = JSON.parse(this._getChartTemplate()({
        data: JSON.stringify([]),
        xColumn: JSON.stringify(''),
        yColumn: JSON.stringify(''),
        xLabel: JSON.stringify(''),
        yLabel: JSON.stringify(''),
        width: JSON.stringify(''),
        height: JSON.stringify('')
      }));

      var containerDimensions = this.chartContainer.getBoundingClientRect();

      var padding = Object.assign({}, chartTemplate.padding, {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      });

      var width = Math.round(containerDimensions.width - padding.left - padding.right);
      var height = Math.round(width * this.options.chartRatio);

      // We save the current dimensions of the chart to diff them whenever the window is resized in order to minimize
      // the number of re-renders
      /* eslint-disable no-underscore-dangle */
      this.options._width = width;
      this.options._height = height;
      /* eslint-enable no-underscore-dangle */

      return {
        width: width,
        height: height
      };
    },

    /**
     * Get the chart Handlebars template
     * @returns {function}
     */
    _getChartTemplate: function () {
      if (!this.options.data.length) return HandlebarsTemplates['front/charts/empty'];
      return HandlebarsTemplates['front/charts/' + this.options.chart];
    },

    /**
     * Generate the vega spec
     * @returns {object}
     */
    _generateVegaSpec: function () {
      if (this.options.data.length && !this.options.chart) {
        var availableCharts = this.widgetToolbox.getAvailableCharts();
        if (availableCharts.length) {
          this.options.chart = availableCharts[0];
        } else {
          // eslint-disable-next-line no-console
          console.warn('Unable to generate a chart out of the current dataset');
          return {};
        }
      }

      var chartDimensions = this._computeChartDimensions();

      // We check if we need to automatically select the columns
      if (this.options.data.length && !this.options.columnX && !this.options.columnY) {
        var columns = this.widgetToolbox.getChartRandomColumns(this.options.chart);
        this.options.columnX = columns.x;
        this.options.columnY = columns.y;
      }
      const columnX = JSON.stringify(this.options.columnX);
      const columnY = JSON.stringify(this.options.columnY);
      const labelX = this.options.xLabel;
      const labelY = this.options.yLabel;
      var xLabel = columnX;
      var yLabel = columnY;

      if (labelX !== undefined && labelX !== null && labelX !== ''){
        xLabel = JSON.stringify(labelX);}

      if (labelY !== undefined && labelY !== null && labelY !== ''){
        yLabel = JSON.stringify(labelY);}

      return this._getChartTemplate()({
        data: JSON.stringify(this.options.data),
        xColumn: columnX,
        yColumn: columnY,
        width: chartDimensions.width,
        height: chartDimensions.height,
        xLabel: xLabel,
        yLabel: yLabel
      });
    },

    /**
     * Create the chart and append it to the DOM
     */
    _renderChart: function () {
      // The widget toolbox could be non-assigned if the view has been instantiated
      // with no data
      if (this.options.data.length && !this.widgetToolbox) {
        this.widgetToolbox = new App.Helper.WidgetToolbox(this.options.data);
      }

      vg.parse
        .spec(JSON.parse(this._generateVegaSpec()), function (error, chart) {
          if (error) {
            App.notifications.broadcast(App.Helper.Notifications.dashboard.chartError)
            return;
          }
          this.chart = chart({
            el: this.chartContainer,
            // By using the SVG renderer, we give the client the posibility to
            // translate the text contained in the charts
            renderer: 'svg'
          }).update();
        }.bind(this));

      // We don't want to trigger anything if the dataset is empty
      if (!this.options.data.length) return;

      // We save the state of the widget each time we render as it can be the
      // consequence of a change in the configuration
      // NOTE: We need to make sure in case the view hasn't been instanciated with
      // a chart configuration that it's set by the toolbox before triggering
      this.trigger('state:change', {
        type: 'chart',
        chart: this.options.chart,
        x: this.options.columnX,
        y: this.options.columnY,
        xLabel: this.options.xLabel,
        yLabel: this.options.yLabel
      });
    },

    /**
     * Create the chart selector and append it to the DOM
     */
    _renderChartSelector: function () {
      var hierarchy = { label: 'Customize chart', options: [] };
      hierarchy.options = this.widgetToolbox.getAvailableCharts().map(function (chartName) {
        var chart = _.findWhere(this.widgetToolbox.getChartConfig(), { name: chartName });
        var acceptedColumnsNb = chart.acceptedStatTypes[0].length;

        var res = { name: chartName, id: chartName };
        res.label = acceptedColumnsNb === 1 ? 'Select a column' : 'Select X column';
        res.options = this.widgetToolbox.getAvailableXColumns(chartName).map(function (xColumn) {
          var o = {};
          o.name = xColumn;
          o.id = xColumn;

          if (acceptedColumnsNb > 1) {
            o.label = 'Select Y column';
            o.options = this.widgetToolbox.getAvailableYColumns(chartName, xColumn).map(function (yColumn) {
              // eslint-disable-next-line no-shadow
              var o = {};
              o.name = yColumn;
              o.id = yColumn;
              return o;
            });
          }

          return o;
        }, this);

        return res;
      }, this);

      new App.View.HierarchicalSelectView({
        el: this.chartSelectorContainer,
        ID: +(new Date()),
        hierarchy: hierarchy,
        onChange: this._onChangeChart.bind(this)
      });
    },

    /**
     * Render the switch button and attach an event listener to it
     */
    _renderSwitchButton: function () {
      // We create the button
      var button = document.createElement('button');
      button.type = 'button';
      button.classList.add('c-button', 'switch-button');
      button.textContent = 'Switch for map';

      // We attach the listener
      button.addEventListener('click', this.options.switchCallback);

      // We append the button to the DOM
      this.el.querySelector('.js-switch-button').appendChild(button);
    },

    /**
     * Render the Custom Axis Label Inputs and attach an event listener to it
     */
    _renderCustomAxisLabelInput: function () {
      const axis = ['X','Y'];

      axis.forEach(function(axis){
        // We create the input
        var input = document.createElement('input');
        input.setAttribute('placeholder', 'Custom ' + axis + ' label');
        input.setAttribute('name', 'custom-' + axis);
        const axisLabel = (axis === 'X') ? this.options.xLabel : this.options.yLabel;
        if (axisLabel !== undefined) { input.value = axisLabel };

        // We attach the listeners
        input.addEventListener('change', function(){
          this._changeAxisLabel(axis , input.value);
        }.bind(this));

        // We append the inputs to the DOM
        this.el.querySelector('#custom-axis-input-container').appendChild(input);

      }.bind(this))
    },
    _changeAxisLabel: function(axis, value){
      if(axis === 'X') this.options.xLabel = value;
      else this.options.yLabel = value;
      this._renderChart();
    },
    /**
     * Remove the changes the component implied to the container and all of
     * its children
     */
    remove: function () {
      this.el.innerHTML = '';
    },

    render: function () {
      this._renderChart();

      // We don't render the chart selector and the switch button if the dataset is empty
      if (this.options.data.length) {
        if (this.options.enableChartSelector){
          this._renderChartSelector();
          // TODO: use displayMode to enable the render of rest of the buttons and inputs
          if(this.options.displayMode !== 'dashboard'){
            this._renderCustomAxisLabelInput();
          }
        }
        if (this.options.switchCallback && typeof this.options.switchCallback === 'function') {
          this._renderSwitchButton();
        }
      }

      return this.el;
    }

  });
})(this.App));
<|endoftext|>


<commit_before>import expect from 'expect';
import rewire from 'rewire';
import * as constants from '../HomePage.constants';

let homeReducer = rewire('../HomePage.reducer');
const initialState = homeReducer.__get__('initialState');
homeReducer = homeReducer.default;

// Test Reducer
describe('defaultReducer', () => {
  // Test that the initial state is returning correctly
  it('should return the initial state', () => {
    expect(homeReducer(undefined, {})).toEqual(initialState);
  });

  // Test that it handles changing the owner correctly
  it('should handle the CHANGE_OWNER_NAME action', () => {
    const name = 'samsmith';

    expect(
      homeReducer({}, {
        type: constants.CHANGE_OWNER_NAME,
        name
      })
    ).toEqual({
      ownerName: name
    });
  });

  // Test that it handles changing the project name correctly
  it('should handle the CHANGE_PROJECT_NAME action', () => {
    const name = 'Webapplication Boilerplate';

    expect(
      homeReducer({}, {
        type: constants.CHANGE_PROJECT_NAME,
        name
      })
    ).toEqual({
      projectName: name
    });
  });
});
<commit_msg>Fix HomePage Reducer test<commit_after>import expect from 'expect';
import rewire from 'rewire';
import * as constants from '../HomePage.constants';
import homeReducer from '../HomePage.reducer';

const rewiredhomeReducer = rewire('../HomePage.reducer');
const initialState = rewiredhomeReducer.__get__('initialState');

// Test Reducer
describe('defaultReducer', () => {
  // Test that the initial state is returning correctly
  it('should return the initial state', () => {
    expect(homeReducer(undefined, {})).toEqual(initialState);
  });

  // Test that it handles changing the owner correctly
  it('should handle the CHANGE_OWNER_NAME action', () => {
    const name = 'samsmith';

    expect(
      homeReducer({}, {
        type: constants.CHANGE_OWNER_NAME,
        name
      })
    ).toEqual({
      ownerName: name
    });
  });

  // Test that it handles changing the project name correctly
  it('should handle the CHANGE_PROJECT_NAME action', () => {
    const name = 'Webapplication Boilerplate';

    expect(
      homeReducer({}, {
        type: constants.CHANGE_PROJECT_NAME,
        name
      })
    ).toEqual({
      projectName: name
    });
  });
});
<|endoftext|>


<commit_before>(function(global, _, Sizzle, smx){

/**
 * Placeholder namespace to contain Node extensions
 * @namespace fn
 * @memberof smx
 */

var fn = {};

////////////////////////////////
// UI ATTRIBUTES INTERFACE
// shortcut for UIAttrController.get
// definend in smx/document/UIAttrController.js

/**
 * UserInterface Methods
 * @module Node/UI
 */

fn.UIAttrInterface = {

    /**
    *   @method ui
    */
    ui: function(key,type){

        return smx.UIAttrController.get(this,key,type);

    }

};

////////////////////////////////
// TIME INTERFACE
// 'time' attributes namespace
// definend in smx/document/TimeAttrController.js

/**
 * Time Interface Methods
 * @module Node/Time
 */
fn.TimeInterface = {

    /**
    *   @method time
    */
    time: function(key){

        return smx.TimeAttrController.get(this,key);
    },

    /**
    *   @method synchronize
    */
    synchronize: function(){

        /*
        //get 'timing' attribute value
        var is_timed = this.time('timed');
        var is_timeline = this.time('timeline');

        //check if node need to be sync
        if (!is_timed && !is_timeline){

            this.duration=0;
            this.start=0;

            //do not use 'sync' attribute so flag it with 'is-sync'
            this[0].setAttribute('is-sync','true');

            return;
        }
        */

        //update sync values (start, duration)
        var force_sync = true;
        var duration = this.time('duration',force_sync);
        var start = this.time('start',force_sync);

        return;
    }

};

//extends smx fn methods
smx.fn = (!smx.fn) ? fn : Object.assign(smx.fn, fn);

})(window, window._, window.Sizzle, window.smx);
<commit_msg>remove lodash dependency<commit_after>(function(global, Sizzle, smx){

/**
 * Placeholder namespace to contain Node extensions
 * @namespace fn
 * @memberof smx
 */

var fn = {};

////////////////////////////////
// UI ATTRIBUTES INTERFACE
// shortcut for UIAttrController.get
// definend in smx/document/UIAttrController.js

/**
 * UserInterface Methods
 * @module Node/UI
 */

fn.UIAttrInterface = {

    /**
    *   @method ui
    */
    ui: function(key,type){

        return smx.UIAttrController.get(this,key,type);

    }

};

////////////////////////////////
// TIME INTERFACE
// 'time' attributes namespace
// definend in smx/document/TimeAttrController.js

/**
 * Time Interface Methods
 * @module Node/Time
 */
fn.TimeInterface = {

    /**
    *   @method time
    */
    time: function(key){

        return smx.TimeAttrController.get(this,key);
    },

    /**
    *   @method synchronize
    */
    synchronize: function(){

        /*
        //get 'timing' attribute value
        var is_timed = this.time('timed');
        var is_timeline = this.time('timeline');

        //check if node need to be sync
        if (!is_timed && !is_timeline){

            this.duration=0;
            this.start=0;

            //do not use 'sync' attribute so flag it with 'is-sync'
            this[0].setAttribute('is-sync','true');

            return;
        }
        */

        //update sync values (start, duration)
        var force_sync = true;
        var duration = this.time('duration',force_sync);
        var start = this.time('start',force_sync);

        return;
    }

};

//extends smx fn methods
smx.fn = (!smx.fn) ? fn : Object.assign(smx.fn, fn);

})(window, window.Sizzle, window.smx);
<|endoftext|>


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

var program = require('commander')
var ncp = require('ncp').ncp
var chalk = require('chalk')
var rl = readline.createInterface(process.stdin, process.stdout)

var parseApk = require('./lib/parseApk')

function success (appPath) {
  console.log(chalk.green('Directory "', appPath, '" created. Copy that directory onto your Chromebook and use "Load unpacked extension" to load the application.'))
  process.exit(0)
}

module.exports = function (callback) {

  program
    .version('4.0.2')
    .option('-t, --tablet', 'Create a tablet version')
    .option('-s, --scale', 'Enable application window scaling')
    .option('-n, --ext-name [value]', 'Extension display name')
    .usage('<path_to_apk_file ...>')
    .parse(process.argv)

  var apk = program.args[0]
  callback = callback || success

  if (!apk) {
    program.outputHelp()
    process.exit(0)
  }

  parseApk(apk, function (err, data) {
    if (err) {
      console.log(chalk.yellow('Failed to load APK'))
    }

    var packageName = null

    try {
      packageName = data.package
    } catch (e) {
      console.log(chalk.yellow('Failed to parse package name in the APK.'))
    }

    if (!packageName) {
      console.log(chalk.yellow('Unknown APK package.'))
      console.log('Please enter the package name (i.e "com.skype.raider", if you get this wrong your app will NOT work): ')
      rl.prompt()
      rl.on('line', function (text) {
        text = text.trim()

        if (/\.apk$/.test(text)) {
          console.log(chalk.red('Package names do not end with .apk'))
          console.log('They usually look like com.application.developer or com.website.www')
          process.exit(0)
        } else if (text.indexOf(' ') !== -1) {
          console.log(chalk.red('Package names do not contain spaces'))
          console.log('They usually look like com.application.developer or com.website.www')
          process.exit(0)
        }
        else {
          createExtension(text)
        }
      })
      .on('close', function () {
        process.exit(0)
      })
    } else {
      createExtension(packageName)
    }

    function createExtension (packageName) {
      var templatePath = path.join(__dirname, '_template')
      var appPath = path.join(packageName + '.android')

      // TODO: refactor this if needed in the future
      ncp(templatePath, appPath, function (err) {
        if (err) {
          throw err
        }

        fs.writeFileSync(path.join(appPath, 'vendor', 'chromium', 'crx', 'custom-android-release-1400197.apk'), fs.readFileSync(apk))

        var manifest = JSON.parse(fs.readFileSync(path.join(templatePath, 'manifest.json')))
        var messages = JSON.parse(fs.readFileSync(path.join(templatePath, '_locales', 'en', 'messages.json')))
        manifest.arc_metadata.name = packageName
        manifest.arc_metadata.packageName = packageName
        manifest.version = '1337'

        if (program.extName) {
          messages.extName.message = program.extName
        } else if (packageName) {
          messages.extName.message = packageName
        } else {
          messages.extName.message = 'App'
        }

        if (program.tablet) {
          manifest.arc_metadata.formFactor = 'tablet'
          manifest.arc_metadata.orientation = 'landscape'
        }

        if (program.scale) {
          manifest.arc_metadata.resize = 'scale'
        }

        fs.writeFileSync(path.join(appPath, 'manifest.json'), JSON.stringify(manifest, null, 2))
        fs.writeFileSync(path.join(appPath, '_locales', 'en', 'messages.json'), JSON.stringify(messages, null, 2))

        // done.
        callback(appPath)
      })
    }
  })

}
<commit_msg>coding standard: fix block must not be padded by blank line<commit_after>var path = require('path')
var fs = require('fs')
var readline = require('readline')

var program = require('commander')
var ncp = require('ncp').ncp
var chalk = require('chalk')
var rl = readline.createInterface(process.stdin, process.stdout)

var parseApk = require('./lib/parseApk')

function success (appPath) {
  console.log(chalk.green('Directory "', appPath, '" created. Copy that directory onto your Chromebook and use "Load unpacked extension" to load the application.'))
  process.exit(0)
}

module.exports = function (callback) {
  program
    .version('4.0.2')
    .option('-t, --tablet', 'Create a tablet version')
    .option('-s, --scale', 'Enable application window scaling')
    .option('-n, --ext-name [value]', 'Extension display name')
    .usage('<path_to_apk_file ...>')
    .parse(process.argv)

  var apk = program.args[0]
  callback = callback || success

  if (!apk) {
    program.outputHelp()
    process.exit(0)
  }

  parseApk(apk, function (err, data) {
    if (err) {
      console.log(chalk.yellow('Failed to load APK'))
    }

    var packageName = null

    try {
      packageName = data.package
    } catch (e) {
      console.log(chalk.yellow('Failed to parse package name in the APK.'))
    }

    if (!packageName) {
      console.log(chalk.yellow('Unknown APK package.'))
      console.log('Please enter the package name (i.e "com.skype.raider", if you get this wrong your app will NOT work): ')
      rl.prompt()
      rl.on('line', function (text) {
        text = text.trim()

        if (/\.apk$/.test(text)) {
          console.log(chalk.red('Package names do not end with .apk'))
          console.log('They usually look like com.application.developer or com.website.www')
          process.exit(0)
        } else if (text.indexOf(' ') !== -1) {
          console.log(chalk.red('Package names do not contain spaces'))
          console.log('They usually look like com.application.developer or com.website.www')
          process.exit(0)
        }
        else {
          createExtension(text)
        }
      })
      .on('close', function () {
        process.exit(0)
      })
    } else {
      createExtension(packageName)
    }

    function createExtension (packageName) {
      var templatePath = path.join(__dirname, '_template')
      var appPath = path.join(packageName + '.android')

      // TODO: refactor this if needed in the future
      ncp(templatePath, appPath, function (err) {
        if (err) {
          throw err
        }

        fs.writeFileSync(path.join(appPath, 'vendor', 'chromium', 'crx', 'custom-android-release-1400197.apk'), fs.readFileSync(apk))

        var manifest = JSON.parse(fs.readFileSync(path.join(templatePath, 'manifest.json')))
        var messages = JSON.parse(fs.readFileSync(path.join(templatePath, '_locales', 'en', 'messages.json')))
        manifest.arc_metadata.name = packageName
        manifest.arc_metadata.packageName = packageName
        manifest.version = '1337'

        if (program.extName) {
          messages.extName.message = program.extName
        } else if (packageName) {
          messages.extName.message = packageName
        } else {
          messages.extName.message = 'App'
        }

        if (program.tablet) {
          manifest.arc_metadata.formFactor = 'tablet'
          manifest.arc_metadata.orientation = 'landscape'
        }

        if (program.scale) {
          manifest.arc_metadata.resize = 'scale'
        }

        fs.writeFileSync(path.join(appPath, 'manifest.json'), JSON.stringify(manifest, null, 2))
        fs.writeFileSync(path.join(appPath, '_locales', 'en', 'messages.json'), JSON.stringify(messages, null, 2))

        // done.
        callback(appPath)
      })
    }
  })
}
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  createCfModel: function createCfModel(model) {
    return {
      Type: 'AWS::ApiGateway::Model',
      Properties: {
        RestApiId: {
          Ref: 'ApiGatewayRestApi',
        },
        ContentType: model.contentType,
        Name: model.name,
        Schema: model.schema || {},
      },
    };
  },

  addModelDependencies: function addModelDependencies(models, resource) {
    Object.keys(models).forEach(contentType => {
      resource.DependsOn.add(`${models[contentType]}Model`);
    });
  },

  addMethodResponses: function addMethodResponses(resource, documentation) {
    if (documentation.methodResponses) {
      if (!resource.Properties.MethodResponses) {
        resource.Properties.MethodResponses = [];
      }

      documentation.methodResponses.forEach(response => {
        let _response = resource.Properties.MethodResponses
          .find(originalResponse => originalResponse.StatusCode === response.statusCode);

        if (!_response) {
          _response = {
            StatusCode: response.statusCode,
          };

          if (response.responseHeaders) {
            const methodResponseHeaders = {};
            response.responseHeaders.forEach(header => {
              methodResponseHeaders[`method.response.header.${header.name}`] = true
            });
            _response.ResponseParameters = methodResponseHeaders;
          }

          resource.Properties.MethodResponses.push(_response);
        }

        _response.ResponseModels = response.responseModels;
        this.addModelDependencies(_response.ResponseModels, resource);
      });
    }
  },

  addRequestModels: function addRequestModels(resource, documentation) {
    if (documentation.requestModels && Object.keys(documentation.requestModels).length > 0) {
      this.addModelDependencies(documentation.requestModels, resource);
      resource.Properties.RequestModels = documentation.requestModels;
    }
  },

  updateCfTemplateFromHttp: function updateCfTemplateFromHttp(eventTypes) {
    if (eventTypes.http && eventTypes.http.documentation) {
      const resourceName = this.normalizePath(eventTypes.http.path);
      const methodLogicalId = this.getMethodLogicalId(resourceName, eventTypes.http.method);
      const resource = this.cfTemplate.Resources[methodLogicalId];

      resource.DependsOn = new Set();
      this.addMethodResponses(resource, eventTypes.http.documentation);
      this.addRequestModels(resource, eventTypes.http.documentation);
      resource.DependsOn = Array.from(resource.DependsOn);
      if (resource.DependsOn.length === 0) {
        delete resource.DependsOn;
      }
    }
  },
};
<commit_msg>feat: responseModels isn't mandatory in responseMethods (#26)<commit_after>'use strict';

module.exports = {
  createCfModel: function createCfModel(model) {
    return {
      Type: 'AWS::ApiGateway::Model',
      Properties: {
        RestApiId: {
          Ref: 'ApiGatewayRestApi',
        },
        ContentType: model.contentType,
        Name: model.name,
        Schema: model.schema || {},
      },
    };
  },

  addModelDependencies: function addModelDependencies(models, resource) {
    Object.keys(models).forEach(contentType => {
      resource.DependsOn.add(`${models[contentType]}Model`);
    });
  },

  addMethodResponses: function addMethodResponses(resource, documentation) {
    if (documentation.methodResponses) {
      if (!resource.Properties.MethodResponses) {
        resource.Properties.MethodResponses = [];
      }

      documentation.methodResponses.forEach(response => {
        let _response = resource.Properties.MethodResponses
          .find(originalResponse => originalResponse.StatusCode === response.statusCode);

        if (!_response) {
          _response = {
            StatusCode: response.statusCode,
          };

          if (response.responseHeaders) {
            const methodResponseHeaders = {};
            response.responseHeaders.forEach(header => {
              methodResponseHeaders[`method.response.header.${header.name}`] = true
            });
            _response.ResponseParameters = methodResponseHeaders;
          }

          resource.Properties.MethodResponses.push(_response);
        }

        if (response.responseModels) {
          _response.ResponseModels = response.responseModels;
          this.addModelDependencies(_response.ResponseModels, resource);
        }
      });
    }
  },

  addRequestModels: function addRequestModels(resource, documentation) {
    if (documentation.requestModels && Object.keys(documentation.requestModels).length > 0) {
      this.addModelDependencies(documentation.requestModels, resource);
      resource.Properties.RequestModels = documentation.requestModels;
    }
  },

  updateCfTemplateFromHttp: function updateCfTemplateFromHttp(eventTypes) {
    if (eventTypes.http && eventTypes.http.documentation) {
      const resourceName = this.normalizePath(eventTypes.http.path);
      const methodLogicalId = this.getMethodLogicalId(resourceName, eventTypes.http.method);
      const resource = this.cfTemplate.Resources[methodLogicalId];

      resource.DependsOn = new Set();
      this.addMethodResponses(resource, eventTypes.http.documentation);
      this.addRequestModels(resource, eventTypes.http.documentation);
      resource.DependsOn = Array.from(resource.DependsOn);
      if (resource.DependsOn.length === 0) {
        delete resource.DependsOn;
      }
    }
  },
};
<|endoftext|>


<commit_before>/** @license MIT License (c) Copyright (c) 2014 Julio Makdisse Saito */

/**
 * Mogger
 * Meld + Trace + Colorful logger
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * @author Julio Makdisse Saito (saitodisse@gmail.com)
 * @version 0.5.0
 */

 (function(root, factory) {

  // AMD
  if (typeof define === 'function' && define.amd) {
    define(['exports',
            'colorful-logger',
            'meld',
            'meldTrace',
            'lodash'
          ],
          function (
            exports,
            ColorfulLogger,
            meld,
            meldTrace,
            _
    ){
      factory(root, exports, ColorfulLogger, meld, meldTrace, _);
    });

  // Node.js
  } else if (typeof exports !== 'undefined') {
    var ColorfulLogger = require('colorful-logger');
    var meld = require('meld');
    var meldTrace = require('meld/aspect/trace');
    var _ = require('lodash');
    factory(root, exports, ColorfulLogger, meld, meldTrace, _);
  }

}(this, function(root, Mogger, ColorfulLogger, meld, meldTrace, _) {

  var globalTimeoutLogId = null;
  var setParentTimeout = function(logger) {
    globalTimeoutLogId = setTimeout(function (){
      logger.log('----------------------------------pause--------------------------');
    }.bind(this), 100);
  };

  /*******************************
    Mogger.Tracer
  *******************************/
  Mogger.Tracer = function (config){
    // global configuration
    config = config || {};
    this.globalConfig = config;
    var getGlobalConfig = function() {
      return this.globalConfig;
    }.bind(this);

    //get logger and send configuration to him
    var logger = config.logger;
    var loggerConfig = config.loggerConfig;
    logger = this.logger = logger || new ColorfulLogger.Logger(loggerConfig);

    //showPause when to much time without any log
    var showPause = config.showPause || false;

    /*
      interceptorsObj:
      {
        globalInterceptors: config.interceptors,
        localInterceptors: options.interceptors,
        info: info
      }
    */
    var checkExistingInterceptors = function(interceptorsObj) {
      var hasLocalInterceptors = !_.isUndefined(interceptorsObj.localInterceptors);
      var hasGlobalInterceptors = !_.isUndefined(interceptorsObj.globalInterceptors);
      return (hasLocalInterceptors || hasGlobalInterceptors);
    };

    var matchInterceptor = function(interceptor, methodName) {
      var filterRegex = interceptor.filterRegex;
      var matchFilterResult = filterRegex.test(methodName);
      if(matchFilterResult){
        return interceptor;
      }
      return false;
    };

    var selectInterceptor = function(interceptor, methodName) {
      // interceptor Array
      if(interceptor && _.isArray(interceptor)){
        for (var i = 0; i < interceptor.length; i++) {
          var interceptorItem = interceptor[i];
          if( matchInterceptor(interceptorItem, methodName) ){
            return interceptorItem;
          }
        }
      }
      // interceptor single obj
      else if(interceptor && !_.isArray(interceptor)){
        return matchInterceptor(interceptor, methodName);
      }

      // no filter match
      return false;
    };

    var applyInterceptor = function(interceptorsObj, interceptor) {
      return interceptor.callback(interceptorsObj.info);
    };

    var checkApplyInterceptors = function(interceptorsObj) {
      if(!checkExistingInterceptors(interceptorsObj)){
        return interceptorsObj.info.method;
      }

      var interceptor = selectInterceptor(interceptorsObj.localInterceptors,  interceptorsObj.info.method);
      if (interceptor === false) {
        interceptor = selectInterceptor(interceptorsObj.globalInterceptors, interceptorsObj.info.method);
      }

      if (interceptor) {
        return applyInterceptor(interceptorsObj, interceptor);
      }
      else {
        return interceptorsObj.info.method;
      }
    };

    var defaults = _.partialRight(_.assign, function(a, b) {
      return typeof a == 'undefined' ? b : a;
    });

    var checkRelevantArguments = function(args) {
      if(args.length === 0){
        return false;
      }

      for (var i = 0; i < args.length; i++) {
        var argument = args[i];

        var isString = _.isString(argument) && argument.length > 0;
        var isNumber = _.isNumber(argument);
        var isBoolean = _.isBoolean(argument);
        var isArray = _.isArray(argument) && argument.length > 0;
        var isEmpty = _.isEmpty(argument);
        var isObject = _.isObject(argument);

        // console.log('<', argument, '>',
        //             '\nisString = ', isString,
        //             '\nisNumber = ', isNumber,
        //             '\nisBoolean = ', isBoolean,
        //             '\nisArray = ', isArray,
        //             '\nisEmpty = ', isEmpty,
        //             '\nisObject = ', isObject
        //             );

        var hasValues = isString || isNumber || isBoolean || isArray || (isObject && !isEmpty);
        if(hasValues){
          return true;
        }
      }
      return false;
    };

    var GetReporter = function (options) {
      this.onCall = function(info) {
        var logs = [],
            targetLog,
            mainMessage = info.method,
            isDisabled = getGlobalConfig().enabled === false || options.enabled === false,
            isIgnored = options.ignorePattern && options.ignorePattern.test(info.method),
            beforeConfig,
            targetConfig,
            interceptorsObj,
            wasModifiedByInterceptor,
            willLogArguments,
            showArguments = !_.isUndefined(options.showArguments) ? options.showArguments : config.showArguments
        ;

        if(isDisabled || isIgnored){
          return false;
        }

        /*
            before (first column / namespace)
        */
        beforeConfig = defaults(options.before, getGlobalConfig().before);
        if(beforeConfig){
          logs.push(beforeConfig);
        }

        /*
            Interceptors
        */
        interceptorsObj = {
          globalInterceptors: config.interceptors,
          localInterceptors: options.interceptors,
          info: info
        };
        mainMessage = checkApplyInterceptors(interceptorsObj);
        wasModifiedByInterceptor = (mainMessage !== info.method);

        /*
            targetConfig local or global
        */
        if(typeof getGlobalConfig().targetConfig !== 'undefined' && typeof options.targetConfig === 'undefined'){
          targetConfig = getGlobalConfig().targetConfig;
        }
        else{
          targetConfig = defaults(options.targetConfig, getGlobalConfig().targetConfig);
        }

        /*
            target (function)
        */
        if(targetConfig){
          targetLog = targetConfig;
          targetLog.message = mainMessage;
        }
        else{
          targetLog = {
            message: mainMessage
          };
        }
        logs.push(targetLog);

        /*
            Function arguments in a groupCollapsed
        */
        willLogArguments = showArguments &&
                           !wasModifiedByInterceptor &&
                           checkRelevantArguments(info.args);

        if(willLogArguments){
          logs[0].logType = 'groupCollapsed';
          logger.log(logs);

          logger.log({
            message: info.args
          });
          logger.log({
            logType: 'groupEnd'
          });
        }
        else{
          logs[0].logType = 'log';
          logger.log(logs);
        }

        /*
            pause
        */
        if(showPause){
          // cancel pause made before
          clearTimeout(globalTimeoutLogId);
          // if is not canceled, it shows the line bellow
          setParentTimeout(logger);
        }

      }.bind(this);

      // other things that can be catch in the future
      //    onReturn:
      //    onThrow:
    };

    this.targets = [];

    // ----------------------------
    // traceObj
    // ----------------------------
    // register one object to be monitored
    // each function will be catched here thanks to AOP meld/aspect/trace
    // ----------------------------
		this.traceObj = function(opt) {
      var reporter = new GetReporter(opt);
      var target = opt.target;

      // gets target real object from surrogateTargets,
      // only if its is an string
      var surrogateTargets = config.surrogateTargets;
      if(surrogateTargets && _.isString(target)){
        target = surrogateTargets[target];
      }

      this.targets.push({
        meldRemover: meld(target, /./, meldTrace(reporter)),
        options: opt
      });
		};

    // ----------------------------
    // removeAllTraces
    // ----------------------------
    // loop over all target and remove them from meld AOP tracer
    // ----------------------------
    this.removeAllTraces = function() {
      this.targets.forEach(function(target) {
        target.meldRemover && target.meldRemover.remove();
      });
    };

	};

  return Mogger;

}));

<commit_msg>UMD, node, require and globals<commit_after>/** @license MIT License (c) Copyright (c) 2014 Julio Makdisse Saito */

/**
 * Mogger
 * Meld + Trace + Colorful logger
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * @author Julio Makdisse Saito (saitodisse@gmail.com)
 * @version 0.5.0
 */

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['exports',
            'colorful-logger',
            'meld',
            'meldTrace',
            'lodash'
          ],
          function (
            exports,
            ColorfulLogger,
            meld,
            meldTrace,
            _
    ){
      factory(root, exports, ColorfulLogger, meld, meldTrace, _);
    });
  } else if (typeof exports === 'object') {
    // CommonJS
    var ColorfulLogger = require('colorful-logger');
    var meld = require('meld');
    var meldTrace = require('meld/aspect/trace');
    var _ = require('lodash');
    factory(root, exports, ColorfulLogger, meld, meldTrace, _);
  } else {
    // Browser globals
    factory((root.Mogger = {}), root.ColorfulLogger, root.meld, root.meldTrace, root._);
  }
}(this, function(root, Mogger, ColorfulLogger, meld, meldTrace, _) {

  var globalTimeoutLogId = null;
  var setParentTimeout = function(logger) {
    globalTimeoutLogId = setTimeout(function (){
      logger.log('----------------------------------pause--------------------------');
    }.bind(this), 100);
  };

  /*******************************
    Mogger.Tracer
  *******************************/
  Mogger.Tracer = function (config){
    // global configuration
    config = config || {};
    this.globalConfig = config;
    var getGlobalConfig = function() {
      return this.globalConfig;
    }.bind(this);

    //get logger and send configuration to him
    var logger = config.logger;
    var loggerConfig = config.loggerConfig;
    logger = this.logger = logger || new ColorfulLogger.Logger(loggerConfig);

    //showPause when to much time without any log
    var showPause = config.showPause || false;

    /*
      interceptorsObj:
      {
        globalInterceptors: config.interceptors,
        localInterceptors: options.interceptors,
        info: info
      }
    */
    var checkExistingInterceptors = function(interceptorsObj) {
      var hasLocalInterceptors = !_.isUndefined(interceptorsObj.localInterceptors);
      var hasGlobalInterceptors = !_.isUndefined(interceptorsObj.globalInterceptors);
      return (hasLocalInterceptors || hasGlobalInterceptors);
    };

    var matchInterceptor = function(interceptor, methodName) {
      var filterRegex = interceptor.filterRegex;
      var matchFilterResult = filterRegex.test(methodName);
      if(matchFilterResult){
        return interceptor;
      }
      return false;
    };

    var selectInterceptor = function(interceptor, methodName) {
      // interceptor Array
      if(interceptor && _.isArray(interceptor)){
        for (var i = 0; i < interceptor.length; i++) {
          var interceptorItem = interceptor[i];
          if( matchInterceptor(interceptorItem, methodName) ){
            return interceptorItem;
          }
        }
      }
      // interceptor single obj
      else if(interceptor && !_.isArray(interceptor)){
        return matchInterceptor(interceptor, methodName);
      }

      // no filter match
      return false;
    };

    var applyInterceptor = function(interceptorsObj, interceptor) {
      return interceptor.callback(interceptorsObj.info);
    };

    var checkApplyInterceptors = function(interceptorsObj) {
      if(!checkExistingInterceptors(interceptorsObj)){
        return interceptorsObj.info.method;
      }

      var interceptor = selectInterceptor(interceptorsObj.localInterceptors,  interceptorsObj.info.method);
      if (interceptor === false) {
        interceptor = selectInterceptor(interceptorsObj.globalInterceptors, interceptorsObj.info.method);
      }

      if (interceptor) {
        return applyInterceptor(interceptorsObj, interceptor);
      }
      else {
        return interceptorsObj.info.method;
      }
    };

    var defaults = _.partialRight(_.assign, function(a, b) {
      return typeof a == 'undefined' ? b : a;
    });

    var checkRelevantArguments = function(args) {
      if(args.length === 0){
        return false;
      }

      for (var i = 0; i < args.length; i++) {
        var argument = args[i];

        var isString = _.isString(argument) && argument.length > 0;
        var isNumber = _.isNumber(argument);
        var isBoolean = _.isBoolean(argument);
        var isArray = _.isArray(argument) && argument.length > 0;
        var isEmpty = _.isEmpty(argument);
        var isObject = _.isObject(argument);

        // console.log('<', argument, '>',
        //             '\nisString = ', isString,
        //             '\nisNumber = ', isNumber,
        //             '\nisBoolean = ', isBoolean,
        //             '\nisArray = ', isArray,
        //             '\nisEmpty = ', isEmpty,
        //             '\nisObject = ', isObject
        //             );

        var hasValues = isString || isNumber || isBoolean || isArray || (isObject && !isEmpty);
        if(hasValues){
          return true;
        }
      }
      return false;
    };

    var GetReporter = function (options) {
      this.onCall = function(info) {
        var logs = [],
            targetLog,
            mainMessage = info.method,
            isDisabled = getGlobalConfig().enabled === false || options.enabled === false,
            isIgnored = options.ignorePattern && options.ignorePattern.test(info.method),
            beforeConfig,
            targetConfig,
            interceptorsObj,
            wasModifiedByInterceptor,
            willLogArguments,
            showArguments = !_.isUndefined(options.showArguments) ? options.showArguments : config.showArguments
        ;

        if(isDisabled || isIgnored){
          return false;
        }

        /*
            before (first column / namespace)
        */
        beforeConfig = defaults(options.before, getGlobalConfig().before);
        if(beforeConfig){
          logs.push(beforeConfig);
        }

        /*
            Interceptors
        */
        interceptorsObj = {
          globalInterceptors: config.interceptors,
          localInterceptors: options.interceptors,
          info: info
        };
        mainMessage = checkApplyInterceptors(interceptorsObj);
        wasModifiedByInterceptor = (mainMessage !== info.method);

        /*
            targetConfig local or global
        */
        if(typeof getGlobalConfig().targetConfig !== 'undefined' && typeof options.targetConfig === 'undefined'){
          targetConfig = getGlobalConfig().targetConfig;
        }
        else{
          targetConfig = defaults(options.targetConfig, getGlobalConfig().targetConfig);
        }

        /*
            target (function)
        */
        if(targetConfig){
          targetLog = targetConfig;
          targetLog.message = mainMessage;
        }
        else{
          targetLog = {
            message: mainMessage
          };
        }
        logs.push(targetLog);

        /*
            Function arguments in a groupCollapsed
        */
        willLogArguments = showArguments &&
                           !wasModifiedByInterceptor &&
                           checkRelevantArguments(info.args);

        if(willLogArguments){
          logs[0].logType = 'groupCollapsed';
          logger.log(logs);

          logger.log({
            message: info.args
          });
          logger.log({
            logType: 'groupEnd'
          });
        }
        else{
          logs[0].logType = 'log';
          logger.log(logs);
        }

        /*
            pause
        */
        if(showPause){
          // cancel pause made before
          clearTimeout(globalTimeoutLogId);
          // if is not canceled, it shows the line bellow
          setParentTimeout(logger);
        }

      }.bind(this);

      // other things that can be catch in the future
      //    onReturn:
      //    onThrow:
    };

    this.targets = [];

    // ----------------------------
    // traceObj
    // ----------------------------
    // register one object to be monitored
    // each function will be catched here thanks to AOP meld/aspect/trace
    // ----------------------------
		this.traceObj = function(opt) {
      var reporter = new GetReporter(opt);
      var target = opt.target;

      // gets target real object from surrogateTargets,
      // only if its is an string
      var surrogateTargets = config.surrogateTargets;
      if(surrogateTargets && _.isString(target)){
        target = surrogateTargets[target];
      }

      this.targets.push({
        meldRemover: meld(target, /./, meldTrace(reporter)),
        options: opt
      });
		};

    // ----------------------------
    // removeAllTraces
    // ----------------------------
    // loop over all target and remove them from meld AOP tracer
    // ----------------------------
    this.removeAllTraces = function() {
      this.targets.forEach(function(target) {
        target.meldRemover && target.meldRemover.remove();
      });
    };

	};

  return Mogger;

}));

<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/* eslint-disable import/prefer-default-export */
import { parsePositiveInteger, newSortDataBy } from '../../../utilities';
import { createRecord } from '../../../database/utilities/index';

/**
 * Immutably clears the current focus
 * @param {object} currState  the copy of state you want affected
 * @return {object}           New state with no cell focused
 */
const clearFocus = state => {
  const { dataState, currentFocusedRowKey } = state;
  if (!currentFocusedRowKey) {
    return state;
  }

  const newDataState = new Map(dataState);
  const currentRowState = newDataState.get(currentFocusedRowKey);
  newDataState.set(currentFocusedRowKey, {
    ...currentRowState,
    focusedColumn: null,
  });

  return { ...state, dataState: newDataState, currentFocusedRowKey: null };
};

/**
 * Immutably sets the current focus to the cell identified by `rowKey` and `columnKey`
 * @param {object} state  The copy of state to affect
 * @param {string} rowKey     The key of the row the cell to focus is in
 * @param {string} columnKey  The key of the column the cell to focus is in
 * @return {object}           A new object with a cell focused
 */
const setFocus = (state, rowKey, columnKey) => {
  const { dataState, currentFocusedRowKey } = state;
  const newDataState = new Map(dataState);

  // Clear previous focus if in a different row
  if (currentFocusedRowKey && rowKey !== currentFocusedRowKey) {
    const currentRowState = newDataState.get(currentFocusedRowKey);
    newDataState.set(currentFocusedRowKey, {
      ...currentRowState,
      focusedColumn: null,
    });
  }

  // Update focusedColumn in specified row
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
    focusedColumn: columnKey,
  });

  return {
    ...state,
    dataState: newDataState,
    currentFocusedRowKey: rowKey,
  };
};

/**
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const filterData = (state, action) => {
  const { backingData, filterDataKeys, sortBy, isAscending } = state;
  const { searchTerm } = action;

  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  const queryString = filterDataKeys
    .map(filterTerm => `${filterTerm} BEGINSWITH[c]  $0`)
    .join(' OR ');

  const newData = newSortDataBy(
    backingData.filtered(queryString, searchTerm).slice(),
    sortBy,
    columnKeyToDataType[sortBy],
    isAscending
  );
  return { ...state, data: newData };
};

export const editTotalQuantity = (state, action) => {
  const { value, rowKey } = action;
  const { data, database, dataState, keyExtractor } = state;

  const objectToEdit = data.find(row => keyExtractor(row) === rowKey);

  database.write(() => {
    objectToEdit.setTotalQuantity(database, parsePositiveInteger(Number(value)));
    database.save('TransactionItem', objectToEdit);
  });

  // Change object reference of row in `dataState` to trigger rerender of that row.
  // Realm object reference in `data` can't be affected in any tidy manner.
  const newDataState = new Map(dataState);
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
  });

  return {
    ...state,
    dataState: newDataState,
  };
};

/**
 * Focus the next appropriate cell after the current cell provided in action
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const focusNextCell = (state, action) => {
  const { data, columns, keyExtractor } = state;
  const { rowKey, columnKey } = action;

  // Handle finding next cell to focus
  let nextEditableColKey;
  const currentColIndex = columns.findIndex(col => col.key === columnKey);
  for (let index = currentColIndex + 1; index < columns.length; index++) {
    if (columns[index].type === 'editable') {
      nextEditableColKey = columns[index].key;
      break;
    }
  }

  if (nextEditableColKey) {
    // Focus next editable cell in row
    return setFocus(state, rowKey, nextEditableColKey);
  }

  // Attempt moving focus to next row
  const nextRowIndex = data.findIndex(row => keyExtractor(row) === rowKey) + 1;

  if (nextRowIndex < data.length) {
    // Focus first editable cell in next row
    const nextRowKey = keyExtractor(data[nextRowIndex]);
    const firstEditableColKey = columns.find(col => col.type === 'editable').key;
    return setFocus(state, nextRowKey, firstEditableColKey);
  }

  // We were on the last row and last column, so unfocus current cell
  return clearFocus(state);
};

/**
 * Sets the provided row in action as selected
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'selectRow', rowKey }
 */
export const selectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: true,
  });

  return { ...state, dataState: newDataState, hasSelection: true };
};

/**
 * Removes the provided row in action from being
 * selected.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'deselectRow', rowKey }
 */
export const deselectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: false,
  });

  let hasSelection = false;
  // eslint-disable-next-line no-restricted-syntax
  for (const row of newDataState.values()) {
    if (row.isSelected) {
      hasSelection = true;
      break;
    }
  }

  return { ...state, dataState: newDataState, hasSelection };
};

/**
 * Removes all rows from being selected.
 * @param {Object} state  The current state
 * Action: { type: 'deselectAll' }
 */
export const deselectAll = state => {
  const { dataState } = state;
  const newDataState = new Map(dataState);

  // eslint-disable-next-line no-restricted-syntax
  for (const [rowKey, rowState] of newDataState.entries()) {
    if (rowState.isSelected) {
      newDataState.set(rowKey, {
        ...rowState,
        isSelected: false,
      });
    }
  }
  return { ...state, dataState: newDataState, hasSelection: false };
};

/**
 * Focuses the cell provided in the action passed
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * action: { type: 'focusCell', rowKey, columnKey }
 */
export const focusCell = (state, action) => {
  // Clear any existing focus and focus cell specified in action
  const { rowKey, columnKey } = action;
  return setFocus(state, rowKey, columnKey);
};

/**
 * Sorts the current set of data by the provided
 * key and direction in action.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'sortData', sortBy }
 * sortBy: String key of the field of the objects to sort by.
 */
export const sortData = (state, action) => {
  const { data, isAscending, sortBy } = state;
  const { sortBy: newSortBy } = action;
  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  // If the new sortBy is the same as the sortBy in state, then invert isAscending
  // that was set by the last sortBy action. Otherwise, default to true.
  const newIsAscending = newSortBy === sortBy ? !isAscending : true;

  const newData = newSortDataBy(data, newSortBy, columnKeyToDataType[newSortBy], newIsAscending);
  return { ...state, data: newData, sortBy: newSortBy, isAscending: newIsAscending };
};

/**
 * Opens a basic modal which does not require any other value
 * to be set upon opening. i.e. text editors
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'openBasicModal', modalKey }
 */
export const openBasicModal = (state, action) => {
  const { modalKey } = action;
  return { ...state, modalKey };
};

/**
 * Sets the modal open state to false, closing any
 * modal that is open.
 *
 * @param {Object} state  The current state
 * Action: { type: 'closeBasicModal' }
 */
export const closeBasicModal = state => ({ ...state, modalKey: '' });

/**
 * Adds all items from a master list to the pageObject held
 * in state - either a Requisition or Transaction.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'addMasterListItems', objectType }
 */
export const addMasterListItems = (state, action) => {
  const { pageObject, database, backingData } = state;
  const { objectType } = action;

  database.write(() => {
    pageObject.addItemsFromMasterList(database);
    database.save(objectType, pageObject);
  });

  const newData = backingData.slice();

  return { ...state, data: newData };
};

/**
 * Creates an Item (Either Requisition or Transaction), and appends
 * this item to the data array for a page.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'addItem', item, addedItemType }
 */
export const addItem = (state, action) => {
  const { database, pageObject, data } = state;
  const { item, addedItemType } = action;
  let addedItem;

  database.write(() => {
    if (pageObject.hasItem(item)) return;
    addedItem = createRecord(database, addedItemType, pageObject, item);
  });

  if (addedItem) return { ...state, data: [addedItem, ...data], modalKey: '' };

  return { ...state, modalKey: '' };
};

/**
 * Reducer helper method for editing a non-setter field on a
 * realm object. Should be called by more explicit reducer
 * methods and not exported to avoid misuse. To create a reducer
 * method for setting a simple value for a realm object, create a
 * more explicit reducer method with logic specific to that field
 * contained, and call this method. See editTheirRef.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, field, pageObjectType }
 */
const editPageObject = (state, action) => {
  const { database, pageObject } = state;
  const { value, pageObjectType, field } = action;

  database.write(() => {
    pageObject[field] = value;
    database.save(pageObjectType, pageObject);
  });

  return { ...state, modalKey: '' };
};

/**
 * Edits the passed pageObject 'theirRef' field with the value supplied.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, pageObjectType }
 */
export const editTheirRef = (state, action) => {
  const { pageObject } = state;
  const { value } = action;

  const { theirRef } = pageObject;

  if (theirRef !== value) return { ...editPageObject(state, { ...action, field: 'theirRef' }) };
  return state;
};

/**
 * Edits the passed pageObject 'comment' field with the value supplied.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, pageObjectType }
 */
export const editComment = (state, action) => {
  const { pageObject } = state;
  const { value } = action;

  const { comment } = pageObject;

  if (comment !== value) return { ...editPageObject(state, { ...action, field: 'comment' }) };
  return state;
};

/**
 * Deletes the selected RequisitionItems or
 * TransactionItems held in state. Where each selected
 * item is indicated by DataState[rowKey].isSelected.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const deleteItemsById = (state, action) => {
  const { database, pageObject, data, dataState, hasSelection } = state;
  const { pageObjectType } = action;

  if (!hasSelection) return state;

  const itemsIds = Array.from(dataState.keys()).filter(rowKey => dataState.get(rowKey).isSelected);

  database.write(() => {
    pageObject.removeItemsById(database, itemsIds);
    database.save(pageObjectType, pageObject);
  });

  const newDataState = new Map();
  const newData = data.filter(item => item.isValid());

  return {
    ...state,
    data: newData,
    dataState: newDataState,
    hasSelection: false,
    modalKey: '',
  };
};
<commit_msg>Add refreshData method to reducerMethods<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/* eslint-disable import/prefer-default-export */
import { parsePositiveInteger, newSortDataBy } from '../../../utilities';
import { createRecord } from '../../../database/utilities/index';

/**
 * Immutably clears the current focus
 * @param {object} currState  the copy of state you want affected
 * @return {object}           New state with no cell focused
 */
const clearFocus = state => {
  const { dataState, currentFocusedRowKey } = state;
  if (!currentFocusedRowKey) {
    return state;
  }

  const newDataState = new Map(dataState);
  const currentRowState = newDataState.get(currentFocusedRowKey);
  newDataState.set(currentFocusedRowKey, {
    ...currentRowState,
    focusedColumn: null,
  });

  return { ...state, dataState: newDataState, currentFocusedRowKey: null };
};

/**
 * Immutably sets the current focus to the cell identified by `rowKey` and `columnKey`
 * @param {object} state  The copy of state to affect
 * @param {string} rowKey     The key of the row the cell to focus is in
 * @param {string} columnKey  The key of the column the cell to focus is in
 * @return {object}           A new object with a cell focused
 */
const setFocus = (state, rowKey, columnKey) => {
  const { dataState, currentFocusedRowKey } = state;
  const newDataState = new Map(dataState);

  // Clear previous focus if in a different row
  if (currentFocusedRowKey && rowKey !== currentFocusedRowKey) {
    const currentRowState = newDataState.get(currentFocusedRowKey);
    newDataState.set(currentFocusedRowKey, {
      ...currentRowState,
      focusedColumn: null,
    });
  }

  // Update focusedColumn in specified row
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
    focusedColumn: columnKey,
  });

  return {
    ...state,
    dataState: newDataState,
    currentFocusedRowKey: rowKey,
  };
};

/**
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const filterData = (state, action) => {
  const { backingData, filterDataKeys, sortBy, isAscending } = state;
  const { searchTerm } = action;

  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  const queryString = filterDataKeys
    .map(filterTerm => `${filterTerm} BEGINSWITH[c]  $0`)
    .join(' OR ');

  const newData = newSortDataBy(
    backingData.filtered(queryString, searchTerm).slice(),
    sortBy,
    columnKeyToDataType[sortBy],
    isAscending
  );
  return { ...state, data: newData };
};

export const editTotalQuantity = (state, action) => {
  const { value, rowKey } = action;
  const { data, database, dataState, keyExtractor } = state;

  const objectToEdit = data.find(row => keyExtractor(row) === rowKey);

  database.write(() => {
    objectToEdit.setTotalQuantity(database, parsePositiveInteger(Number(value)));
    database.save('TransactionItem', objectToEdit);
  });

  // Change object reference of row in `dataState` to trigger rerender of that row.
  // Realm object reference in `data` can't be affected in any tidy manner.
  const newDataState = new Map(dataState);
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
  });

  return {
    ...state,
    dataState: newDataState,
  };
};

/**
 * Focus the next appropriate cell after the current cell provided in action
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const focusNextCell = (state, action) => {
  const { data, columns, keyExtractor } = state;
  const { rowKey, columnKey } = action;

  // Handle finding next cell to focus
  let nextEditableColKey;
  const currentColIndex = columns.findIndex(col => col.key === columnKey);
  for (let index = currentColIndex + 1; index < columns.length; index++) {
    if (columns[index].type === 'editable') {
      nextEditableColKey = columns[index].key;
      break;
    }
  }

  if (nextEditableColKey) {
    // Focus next editable cell in row
    return setFocus(state, rowKey, nextEditableColKey);
  }

  // Attempt moving focus to next row
  const nextRowIndex = data.findIndex(row => keyExtractor(row) === rowKey) + 1;

  if (nextRowIndex < data.length) {
    // Focus first editable cell in next row
    const nextRowKey = keyExtractor(data[nextRowIndex]);
    const firstEditableColKey = columns.find(col => col.type === 'editable').key;
    return setFocus(state, nextRowKey, firstEditableColKey);
  }

  // We were on the last row and last column, so unfocus current cell
  return clearFocus(state);
};

/**
 * Sets the provided row in action as selected
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'selectRow', rowKey }
 */
export const selectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: true,
  });

  return { ...state, dataState: newDataState, hasSelection: true };
};

/**
 * Removes the provided row in action from being
 * selected.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'deselectRow', rowKey }
 */
export const deselectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: false,
  });

  let hasSelection = false;
  // eslint-disable-next-line no-restricted-syntax
  for (const row of newDataState.values()) {
    if (row.isSelected) {
      hasSelection = true;
      break;
    }
  }

  return { ...state, dataState: newDataState, hasSelection };
};

/**
 * Removes all rows from being selected.
 * @param {Object} state  The current state
 * Action: { type: 'deselectAll' }
 */
export const deselectAll = state => {
  const { dataState } = state;
  const newDataState = new Map(dataState);

  // eslint-disable-next-line no-restricted-syntax
  for (const [rowKey, rowState] of newDataState.entries()) {
    if (rowState.isSelected) {
      newDataState.set(rowKey, {
        ...rowState,
        isSelected: false,
      });
    }
  }
  return { ...state, dataState: newDataState, hasSelection: false };
};

/**
 * Focuses the cell provided in the action passed
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * action: { type: 'focusCell', rowKey, columnKey }
 */
export const focusCell = (state, action) => {
  // Clear any existing focus and focus cell specified in action
  const { rowKey, columnKey } = action;
  return setFocus(state, rowKey, columnKey);
};

/**
 * Sorts the current set of data by the provided
 * key and direction in action.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'sortData', sortBy }
 * sortBy: String key of the field of the objects to sort by.
 */
export const sortData = (state, action) => {
  const { data, isAscending, sortBy } = state;
  const { sortBy: newSortBy } = action;
  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  // If the new sortBy is the same as the sortBy in state, then invert isAscending
  // that was set by the last sortBy action. Otherwise, default to true.
  const newIsAscending = newSortBy === sortBy ? !isAscending : true;

  const newData = newSortDataBy(data, newSortBy, columnKeyToDataType[newSortBy], newIsAscending);
  return { ...state, data: newData, sortBy: newSortBy, isAscending: newIsAscending };
};

/**
 * Opens a basic modal which does not require any other value
 * to be set upon opening. i.e. text editors
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'openBasicModal', modalKey }
 */
export const openBasicModal = (state, action) => {
  const { modalKey } = action;
  return { ...state, modalKey };
};

/**
 * Sets the modal open state to false, closing any
 * modal that is open.
 *
 * @param {Object} state  The current state
 * Action: { type: 'closeBasicModal' }
 */
export const closeBasicModal = state => ({ ...state, modalKey: '' });

/**
 * Adds all items from a master list to the pageObject held
 * in state - either a Requisition or Transaction.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'addMasterListItems', objectType }
 */
export const addMasterListItems = (state, action) => {
  const { pageObject, database, backingData } = state;
  const { objectType } = action;

  database.write(() => {
    pageObject.addItemsFromMasterList(database);
    database.save(objectType, pageObject);
  });

  const newData = backingData.slice();

  return { ...state, data: newData };
};

/**
 * Creates an Item (Either Requisition or Transaction), and appends
 * this item to the data array for a page.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'addItem', item, addedItemType }
 */
export const addItem = (state, action) => {
  const { database, pageObject, data } = state;
  const { item, addedItemType } = action;
  let addedItem;

  database.write(() => {
    if (pageObject.hasItem(item)) return;
    addedItem = createRecord(database, addedItemType, pageObject, item);
  });

  if (addedItem) return { ...state, data: [addedItem, ...data], modalKey: '' };

  return { ...state, modalKey: '' };
};

/**
 * Reducer helper method for editing a non-setter field on a
 * realm object. Should be called by more explicit reducer
 * methods and not exported to avoid misuse. To create a reducer
 * method for setting a simple value for a realm object, create a
 * more explicit reducer method with logic specific to that field
 * contained, and call this method. See editTheirRef.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, field, pageObjectType }
 */
const editPageObject = (state, action) => {
  const { database, pageObject } = state;
  const { value, pageObjectType, field } = action;

  database.write(() => {
    pageObject[field] = value;
    database.save(pageObjectType, pageObject);
  });

  return { ...state, modalKey: '' };
};

/**
 * Edits the passed pageObject 'theirRef' field with the value supplied.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, pageObjectType }
 */
export const editTheirRef = (state, action) => {
  const { pageObject } = state;
  const { value } = action;

  const { theirRef } = pageObject;

  if (theirRef !== value) return { ...editPageObject(state, { ...action, field: 'theirRef' }) };
  return state;
};

/**
 * Edits the passed pageObject 'comment' field with the value supplied.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'editPageObject', value, pageObjectType }
 */
export const editComment = (state, action) => {
  const { pageObject } = state;
  const { value } = action;

  const { comment } = pageObject;

  if (comment !== value) return { ...editPageObject(state, { ...action, field: 'comment' }) };
  return state;
};

/**
 * Deletes the selected RequisitionItems or
 * TransactionItems held in state. Where each selected
 * item is indicated by DataState[rowKey].isSelected.
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const deleteItemsById = (state, action) => {
  const { database, pageObject, data, dataState, hasSelection } = state;
  const { pageObjectType } = action;

  if (!hasSelection) return state;

  const itemsIds = Array.from(dataState.keys()).filter(rowKey => dataState.get(rowKey).isSelected);

  database.write(() => {
    pageObject.removeItemsById(database, itemsIds);
    database.save(pageObjectType, pageObject);
  });

  const newDataState = new Map();
  const newData = data.filter(item => item.isValid());

  return {
    ...state,
    data: newData,
    dataState: newDataState,
    hasSelection: false,
    modalKey: '',
  };
};

/**
 * Simply refresh's the data object in state to
 * correctly match the backingData when side effects
 * such as finalizing manipulate the state of a page
 * from outside the reducer. Should only be used when
 * there are no other options.
 *
 * @param {Object} state  The current state
 */
export const refreshData = state => {
  const { backingData } = state;
  return { ...state, data: backingData.slice() };
};
<|endoftext|>