<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* global nf, Slick */

nf.TemplatesTable = (function () {

    /**
     * Configuration object used to hold a number of configuration items.
     */
    var config = {
        filterText: 'Filter',
        styles: {
            filterList: 'templates-filter-list'
        },
        urls: {
            templates: '../nifi-api/controller/templates'
        }
    };

    /**
     * Sorts the specified data using the specified sort details.
     *
     * @param {object} sortDetails
     * @param {object} data
     */
    var sort = function (sortDetails, data) {
        // defines a function for sorting
        var comparer = function (a, b) {
            if (sortDetails.columnId === 'timestamp') {
                var aDate = nf.Common.parseDateTime(a[sortDetails.columnId]);
                var bDate = nf.Common.parseDateTime(b[sortDetails.columnId]);
                return aDate.getTime() - bDate.getTime();
            } else {
                var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
                var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
                return aString === bString ? 0 : aString > bString ? 1 : -1;
            }
        };

        // perform the sort
        data.sort(comparer, sortDetails.sortAsc);
    };

    /**
     * Prompts the user before attempting to delete the specified template.
     *
     * @argument {object} template     The template
     */
    var promptToDeleteTemplate = function (template) {
        // prompt for deletion
        nf.Dialog.showYesNoDialog({
            dialogContent: 'Delete template \'' + nf.Common.escapeHtml(template.name) + '\'?',
            overlayBackground: false,
            yesHandler: function () {
                deleteTemplate(template.id);
            }
        });
    };

    /**
     * Deletes the template with the specified id.
     *
     * @argument {string} templateId     The template id
     */
    var deleteTemplate = function (templateId) {
        $.ajax({
            type: 'DELETE',
            url: config.urls.templates + '/' + encodeURIComponent(templateId),
            dataType: 'json'
        }).done(function () {
            var templatesGrid = $('#templates-table').data('gridInstance');
            var templatesData = templatesGrid.getData();
            templatesData.deleteItem(templateId);
        }).fail(nf.Common.handleAjaxError);
    };

    /**
     * Get the text out of the filter field. If the filter field doesn't
     * have any text it will contain the text 'filter list' so this method
     * accounts for that.
     */
    var getFilterText = function () {
        var filterText = '';
        var filterField = $('#templates-filter');
        if (!filterField.hasClass(config.styles.filterList)) {
            filterText = filterField.val();
        }
        return filterText;
    };

    /**
     * Applies the filter found in the filter expression text field.
     */
    var applyFilter = function () {
        // get the dataview
        var templatesGrid = $('#templates-table').data('gridInstance');

        // ensure the grid has been initialized
        if (nf.Common.isDefinedAndNotNull(templatesGrid)) {
            var templatesData = templatesGrid.getData();

            // update the search criteria
            templatesData.setFilterArgs({
                searchString: getFilterText(),
                property: $('#templates-filter-type').combo('getSelectedOption').value
            });
            templatesData.refresh();
        }
    };

    /**
     * Performs the filtering.
     *
     * @param {object} item     The item subject to filtering
     * @param {object} args     Filter arguments
     * @returns {Boolean}       Whether or not to include the item
     */
    var filter = function (item, args) {
        if (args.searchString === '') {
            return true;
        }

        try {
            // perform the row filtering
            var filterExp = new RegExp(args.searchString, 'i');
        } catch (e) {
            // invalid regex
            return false;
        }

        // perform the filter
        return item[args.property].search(filterExp) >= 0;
    };

    return {
        /**
         * Initializes the templates list.
         */
        init: function () {
            // define the function for filtering the list
            $('#templates-filter').keyup(function () {
                applyFilter();
            }).focus(function () {
                if ($(this).hasClass(config.styles.filterList)) {
                    $(this).removeClass(config.styles.filterList).val('');
                }
            }).blur(function () {
                if ($(this).val() === '') {
                    $(this).addClass(config.styles.filterList).val(config.filterText);
                }
            }).addClass(config.styles.filterList).val(config.filterText);

            // filter type
            $('#templates-filter-type').combo({
                options: [{
                        text: 'by name',
                        value: 'name'
                    }, {
                        text: 'by description',
                        value: 'description'
                    }],
                select: function (option) {
                    applyFilter();
                }
            });

            // listen for browser resize events to update the page size
            $(window).resize(function () {
                nf.TemplatesTable.resetTableSize();
            });

            // enable template uploading if DFM
            if (nf.Common.isDFM()) {
                $('#upload-template-container').show();
            }

            // function for formatting the last accessed time
            var valueFormatter = function (row, cell, value, columnDef, dataContext) {
                return nf.Common.formatValue(value);
            };

            // function for formatting the actions column
            var actionFormatter = function (row, cell, value, columnDef, dataContext) {
                var markup = '';

                // all DFMs to remove templates
                if (nf.Common.isDFM()) {
                    markup += ' ';
                }
                return markup;
            };

            // initialize the templates table
            var templatesColumns = [
                {id: 'timestamp', name: 'Date/Time', field: 'timestamp', sortable: true, defaultSortAsc: false, resizable: false, formatter: valueFormatter, width: 225, maxWidth: 225},
                {id: 'name', name: 'Name', field: 'name', sortable: true, resizable: true},
                {id: 'description', name: 'Description', field: 'description', sortable: true, resizable: true, formatter: valueFormatter},
                {id: 'actions', name: ' ', sortable: false, resizable: false, formatter: actionFormatter, width: 100, maxWidth: 100}
            ];
            var templatesOptions = {
                forceFitColumns: true,
                enableTextSelectionOnCells: true,
                enableCellNavigation: false,
                enableColumnReorder: false,
                autoEdit: false
            };

            // initialize the dataview
            var templatesData = new Slick.Data.DataView({
                inlineFilters: false
            });
            templatesData.setItems([]);
            templatesData.setFilterArgs({
                searchString: getFilterText(),
                property: $('#templates-filter-type').combo('getSelectedOption').value
            });
            templatesData.setFilter(filter);

            // initialize the sort
            sort({
                columnId: 'timestamp',
                sortAsc: false
            }, templatesData);

            // initialize the grid
            var templatesGrid = new Slick.Grid('#templates-table', templatesData, templatesColumns, templatesOptions);
            templatesGrid.setSelectionModel(new Slick.RowSelectionModel());
            templatesGrid.registerPlugin(new Slick.AutoTooltips());
            templatesGrid.setSortColumn('timestamp', false);
            templatesGrid.onSort.subscribe(function (e, args) {
                sort({
                    columnId: args.sortCol.field,
                    sortAsc: args.sortAsc
                }, templatesData);
            });

            // configure a click listener
            templatesGrid.onClick.subscribe(function (e, args) {
                var target = $(e.target);

                // get the node at this row
                var item = templatesData.getItem(args.row);

                // determine the desired action
                if (templatesGrid.getColumns()[args.cell].id === 'actions') {
                    if (target.hasClass('export-template')) {
                        window.open(config.urls.templates + '/' + encodeURIComponent(item.id));
                    } else if (target.hasClass('prompt-to-delete-template')) {
                        promptToDeleteTemplate(item);
                    }
                }
            });

            // wire up the dataview to the grid
            templatesData.onRowCountChanged.subscribe(function (e, args) {
                templatesGrid.updateRowCount();
                templatesGrid.render();

                // update the total number of displayed processors
                $('#displayed-templates').text(args.current);
            });
            templatesData.onRowsChanged.subscribe(function (e, args) {
                templatesGrid.invalidateRows(args.rows);
                templatesGrid.render();
            });

            // hold onto an instance of the grid
            $('#templates-table').data('gridInstance', templatesGrid);

            // initialize the number of displayed items
            $('#displayed-templates').text('0');
        },

        /**
         * Update the size of the grid based on its container's current size.
         */
        resetTableSize: function () {
            var templateGrid = $('#templates-table').data('gridInstance');
            if (nf.Common.isDefinedAndNotNull(templateGrid)) {
                templateGrid.resizeCanvas();
            }
        },

        /**
         * Load the processor templates table.
         */
        loadTemplatesTable: function () {
            return $.ajax({
                type: 'GET',
                url: config.urls.templates,
                data: {
                    verbose: false
                },
                dataType: 'json'
            }).done(function (response) {
                // ensure there are groups specified
                if (nf.Common.isDefinedAndNotNull(response.templates)) {
                    var templatesGrid = $('#templates-table').data('gridInstance');
                    var templatesData = templatesGrid.getData();

                    // set the items
                    templatesData.setItems(response.templates);
                    templatesData.reSort();
                    templatesGrid.invalidate();

                    // update the stats last refreshed timestamp
                    $('#templates-last-refreshed').text(response.generated);

                    // update the total number of processors
                    $('#total-templates').text(response.templates.length);
                } else {
                    $('#total-templates').text('0');
                }
            }).fail(nf.Common.handleAjaxError);
        }
    };
}());<commit_msg>NIFI-313: - Ensuring the total number of templates remain correct after a deletion.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* global nf, Slick */

nf.TemplatesTable = (function () {

    /**
     * Configuration object used to hold a number of configuration items.
     */
    var config = {
        filterText: 'Filter',
        styles: {
            filterList: 'templates-filter-list'
        },
        urls: {
            templates: '../nifi-api/controller/templates'
        }
    };

    /**
     * Sorts the specified data using the specified sort details.
     *
     * @param {object} sortDetails
     * @param {object} data
     */
    var sort = function (sortDetails, data) {
        // defines a function for sorting
        var comparer = function (a, b) {
            if (sortDetails.columnId === 'timestamp') {
                var aDate = nf.Common.parseDateTime(a[sortDetails.columnId]);
                var bDate = nf.Common.parseDateTime(b[sortDetails.columnId]);
                return aDate.getTime() - bDate.getTime();
            } else {
                var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
                var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
                return aString === bString ? 0 : aString > bString ? 1 : -1;
            }
        };

        // perform the sort
        data.sort(comparer, sortDetails.sortAsc);
    };

    /**
     * Prompts the user before attempting to delete the specified template.
     *
     * @argument {object} template     The template
     */
    var promptToDeleteTemplate = function (template) {
        // prompt for deletion
        nf.Dialog.showYesNoDialog({
            dialogContent: 'Delete template \'' + nf.Common.escapeHtml(template.name) + '\'?',
            overlayBackground: false,
            yesHandler: function () {
                deleteTemplate(template.id);
            }
        });
    };

    /**
     * Deletes the template with the specified id.
     *
     * @argument {string} templateId     The template id
     */
    var deleteTemplate = function (templateId) {
        $.ajax({
            type: 'DELETE',
            url: config.urls.templates + '/' + encodeURIComponent(templateId),
            dataType: 'json'
        }).done(function () {
            var templatesGrid = $('#templates-table').data('gridInstance');
            var templatesData = templatesGrid.getData();
            templatesData.deleteItem(templateId);

            // update the total number of templates
            $('#total-templates').text(templatesData.getItems().length);
        }).fail(nf.Common.handleAjaxError);
    };

    /**
     * Get the text out of the filter field. If the filter field doesn't
     * have any text it will contain the text 'filter list' so this method
     * accounts for that.
     */
    var getFilterText = function () {
        var filterText = '';
        var filterField = $('#templates-filter');
        if (!filterField.hasClass(config.styles.filterList)) {
            filterText = filterField.val();
        }
        return filterText;
    };

    /**
     * Applies the filter found in the filter expression text field.
     */
    var applyFilter = function () {
        // get the dataview
        var templatesGrid = $('#templates-table').data('gridInstance');

        // ensure the grid has been initialized
        if (nf.Common.isDefinedAndNotNull(templatesGrid)) {
            var templatesData = templatesGrid.getData();

            // update the search criteria
            templatesData.setFilterArgs({
                searchString: getFilterText(),
                property: $('#templates-filter-type').combo('getSelectedOption').value
            });
            templatesData.refresh();
        }
    };

    /**
     * Performs the filtering.
     *
     * @param {object} item     The item subject to filtering
     * @param {object} args     Filter arguments
     * @returns {Boolean}       Whether or not to include the item
     */
    var filter = function (item, args) {
        if (args.searchString === '') {
            return true;
        }

        try {
            // perform the row filtering
            var filterExp = new RegExp(args.searchString, 'i');
        } catch (e) {
            // invalid regex
            return false;
        }

        // perform the filter
        return item[args.property].search(filterExp) >= 0;
    };

    return {
        /**
         * Initializes the templates list.
         */
        init: function () {
            // define the function for filtering the list
            $('#templates-filter').keyup(function () {
                applyFilter();
            }).focus(function () {
                if ($(this).hasClass(config.styles.filterList)) {
                    $(this).removeClass(config.styles.filterList).val('');
                }
            }).blur(function () {
                if ($(this).val() === '') {
                    $(this).addClass(config.styles.filterList).val(config.filterText);
                }
            }).addClass(config.styles.filterList).val(config.filterText);

            // filter type
            $('#templates-filter-type').combo({
                options: [{
                        text: 'by name',
                        value: 'name'
                    }, {
                        text: 'by description',
                        value: 'description'
                    }],
                select: function (option) {
                    applyFilter();
                }
            });

            // listen for browser resize events to update the page size
            $(window).resize(function () {
                nf.TemplatesTable.resetTableSize();
            });

            // enable template uploading if DFM
            if (nf.Common.isDFM()) {
                $('#upload-template-container').show();
            }

            // function for formatting the last accessed time
            var valueFormatter = function (row, cell, value, columnDef, dataContext) {
                return nf.Common.formatValue(value);
            };

            // function for formatting the actions column
            var actionFormatter = function (row, cell, value, columnDef, dataContext) {
                var markup = '';

                // all DFMs to remove templates
                if (nf.Common.isDFM()) {
                    markup += ' ';
                }
                return markup;
            };

            // initialize the templates table
            var templatesColumns = [
                {id: 'timestamp', name: 'Date/Time', field: 'timestamp', sortable: true, defaultSortAsc: false, resizable: false, formatter: valueFormatter, width: 225, maxWidth: 225},
                {id: 'name', name: 'Name', field: 'name', sortable: true, resizable: true},
                {id: 'description', name: 'Description', field: 'description', sortable: true, resizable: true, formatter: valueFormatter},
                {id: 'actions', name: ' ', sortable: false, resizable: false, formatter: actionFormatter, width: 100, maxWidth: 100}
            ];
            var templatesOptions = {
                forceFitColumns: true,
                enableTextSelectionOnCells: true,
                enableCellNavigation: false,
                enableColumnReorder: false,
                autoEdit: false
            };

            // initialize the dataview
            var templatesData = new Slick.Data.DataView({
                inlineFilters: false
            });
            templatesData.setItems([]);
            templatesData.setFilterArgs({
                searchString: getFilterText(),
                property: $('#templates-filter-type').combo('getSelectedOption').value
            });
            templatesData.setFilter(filter);

            // initialize the sort
            sort({
                columnId: 'timestamp',
                sortAsc: false
            }, templatesData);

            // initialize the grid
            var templatesGrid = new Slick.Grid('#templates-table', templatesData, templatesColumns, templatesOptions);
            templatesGrid.setSelectionModel(new Slick.RowSelectionModel());
            templatesGrid.registerPlugin(new Slick.AutoTooltips());
            templatesGrid.setSortColumn('timestamp', false);
            templatesGrid.onSort.subscribe(function (e, args) {
                sort({
                    columnId: args.sortCol.field,
                    sortAsc: args.sortAsc
                }, templatesData);
            });

            // configure a click listener
            templatesGrid.onClick.subscribe(function (e, args) {
                var target = $(e.target);

                // get the node at this row
                var item = templatesData.getItem(args.row);

                // determine the desired action
                if (templatesGrid.getColumns()[args.cell].id === 'actions') {
                    if (target.hasClass('export-template')) {
                        window.open(config.urls.templates + '/' + encodeURIComponent(item.id));
                    } else if (target.hasClass('prompt-to-delete-template')) {
                        promptToDeleteTemplate(item);
                    }
                }
            });

            // wire up the dataview to the grid
            templatesData.onRowCountChanged.subscribe(function (e, args) {
                templatesGrid.updateRowCount();
                templatesGrid.render();

                // update the total number of displayed processors
                $('#displayed-templates').text(args.current);
            });
            templatesData.onRowsChanged.subscribe(function (e, args) {
                templatesGrid.invalidateRows(args.rows);
                templatesGrid.render();
            });

            // hold onto an instance of the grid
            $('#templates-table').data('gridInstance', templatesGrid);

            // initialize the number of displayed items
            $('#displayed-templates').text('0');
        },

        /**
         * Update the size of the grid based on its container's current size.
         */
        resetTableSize: function () {
            var templateGrid = $('#templates-table').data('gridInstance');
            if (nf.Common.isDefinedAndNotNull(templateGrid)) {
                templateGrid.resizeCanvas();
            }
        },

        /**
         * Load the processor templates table.
         */
        loadTemplatesTable: function () {
            return $.ajax({
                type: 'GET',
                url: config.urls.templates,
                data: {
                    verbose: false
                },
                dataType: 'json'
            }).done(function (response) {
                // ensure there are groups specified
                if (nf.Common.isDefinedAndNotNull(response.templates)) {
                    var templatesGrid = $('#templates-table').data('gridInstance');
                    var templatesData = templatesGrid.getData();

                    // set the items
                    templatesData.setItems(response.templates);
                    templatesData.reSort();
                    templatesGrid.invalidate();

                    // update the stats last refreshed timestamp
                    $('#templates-last-refreshed').text(response.generated);

                    // update the total number of processors
                    $('#total-templates').text(response.templates.length);
                } else {
                    $('#total-templates').text('0');
                }
            }).fail(nf.Common.handleAjaxError);
        }
    };
}());<|endoftext|>


<commit_before>var Serialport = require('serialport');
var intelhex = require('intel-hex');
var Stk500v1 = require('stk500');
var Stk500v2 = require('stk500-v2');
var avr109 = require('chip.avr.avr109');
var async = require('async');
var fs = require('graceful-fs');
var boards = require('./boards');
var childProcess = require('child_process');
var colors = require('colors');

/**
 * Constructor
 *
 * @param {object} options - options for consumer to pass in
 */
var Avrgirl_arduino = function (opts) {
  var opts = opts || {};

  this.options = {
    debug: opts.debug || false,
    board: opts.board || 'uno',
    port: opts.port || ''
  };

  this.debug = this.options.debug ? console.log : function() {};

  this.chip = undefined;

  // get board properties
  this.board = boards[this.options.board];
  if (!this.board) {
    return;
  }

  // assign the correct module to the protocol of the chosen board
  if (this.board.protocol === 'stk500v1') {
    this.chip = new Stk500v1({quiet: true});
  } else if (this.board.protocol === 'stk500v2') {
    this.chip = Stk500v2;
  } else if (this.board.protocol === 'avr109') {
    this.chip = avr109;
  }
};

/**
 * Create new serialport instance for the Arduino board, but do not immediately connect.
 */
Avrgirl_arduino.prototype._setUpSerial = function () {
  this.serialPort = new Serialport.SerialPort(this.options.port, {
    baudRate: this.board.baud,
  }, false);
};

/**
 * Opens and parses a given hex file
 */
Avrgirl_arduino.prototype._parseHex = function (file) {
  var data = fs.readFileSync(file, {encoding: 'utf8'});
  return intelhex.parse(data).data;
};

/**
 * Public method for flashing a hex file to the main program allocation of the Arduino
 *
 * @param {string} file - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype.flash = function (file, callback) {
  var self = this;
  var hex = file;

  // if we don't have a valid board, we cannot continue.
  if (!this.board) {
    return callback(new Error('"' + this.options.board + '" is not a supported board type.'));
  }

  // if we haven't been supplied an explicit port to connect to, auto sniff one.
  if (this.options.port === '') {
    // if this is a pro-mini, we cannot auto-sniff so return an error
    if (this.options.board === 'pro-mini') {
      var error = new Error('you\'re using a pro-mini, please specify the port in your options.');
      return callback(error);
    }

    this._sniffPort(function (port) {
      if (port !== null) {
        self.debug('found ' + self.options.board + ' on port ' + port);
        // found a port, save it
        self.options.port = port;
        // upload hex
        self._upload(hex, callback);
      } else {
        // we didn't find the board
        return callback(new Error('no Arduino found.'));
      }
    });
  } else {
    // we already know the port, so upload
    this._upload(hex, callback);
  }
};

/**
 * Calls the correct upload method, depending on which protocol the Arduino uses
 *
 * @param {string} hex - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._upload = function (hex, callback) {
  var self = this;
  var eggs = hex;
  var cb = callback;

  if (self.board.protocol === 'stk500v1') {
    self._uploadSTK500v1(eggs, cb);
  }

  else if (self.board.protocol === 'stk500v2') {
    self._uploadSTK500v2(eggs, cb);
  }

  else if (self.board.protocol === 'avr109') {
    self._resetAVR109(function (error) {
      if (error) { return cb(error); }
        self.debug('reset complete.');
        self._uploadAVR109(eggs, cb);
    });
  }
};

/**
 * Upload method for the STK500v1 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadSTK500v1 = function (eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!this.serialPort) {
    this._setUpSerial();
  }

  // open connection
  this.serialPort.open(function (error) {
    if (error) { return callback(error); }

    self.debug('connected');

    // open/parse supplied hex file
    var hex = self._parseHex(eggs);

    self.debug('flashing, please wait...');

    // flash
    self.chip.bootload(self.serialPort, hex, self.board, function (err) {
      var color = (err ? colors.red : colors.green);

      self.debug(color('flash complete.'));

      // Always close the serialport
      self.serialPort.close();

      return callback(err);
    });
  });
};

/**
 * Upload method for the STK500v2 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadSTK500v2 = function (eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!this.serialPort) {
    this._setUpSerial();
  }

  // open connection
  this.serialPort.open(function (error) {
    if (error) { return callback(error); }

    // instantiate stk500v2 with newly open serialport
    self.chip = self.chip(self.serialPort);

    self.debug('connected');

    // open/parse supplied hex file
    var hex = self._parseHex(eggs);

    self.debug('flashing, please wait...');

    async.series([
      function (callback) {
        self.chip.sync(5, callback);
      },
      function (callback) {
        self.chip.verifySignature(self.board.signature, callback);
      },
      function (callback) {
        self.chip.enterProgrammingMode(self.board, callback);
      },
      function (callback) {
        self.chip.upload(hex, self.board.pageSize, callback);
      },
      function (callback) {
        self.chip.exitProgrammingMode(callback);
      }
    ],
    function(err, results) {
      var color = (err ? colors.red : colors.green);

      self.debug(color('flash complete.'));

      // Always close the serialport
      self.serialPort.close();

      return callback(err);
    });
  });
};

/**
 * Software resets an Arduino AVR109 bootloaded chip into bootloader mode
 *
 * Note: this method runs a child process, as it is currently difficult to guarantee
 * that a serialport connection has truly closed via node-serialport.
 * Exiting the child process when done ensures we have a true closure,
 * and therefore a completed board reset.
 *
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._resetAVR109 = function (callback) {
  var self = this;
  var resetFile = __dirname + '/lib/leo-reset.js';
  var tries = 0;

  self.debug('resetting board...');

  childProcess.execFile('node', [resetFile, self.options.port], function() {
    tryConnect(function(connected) {
      var status = connected ? null : new Error('could not complete reset.');
      callback(status);
    });
  });

  // here we have to retry the serialport polling,
  // until the chip boots back up to recreate the virtual com port
  function tryConnect (callback) {
    function checkList() {
      Serialport.list(function (error, ports) {
        // iterate through ports looking for the one port to rule them all
        for (var i = 0; i < ports.length; i++) {
          if (ports[i].comName === self.options.port) {
            return callback(true);
          }
        }
        tries += 1;
        if (tries < 4) {
          setTimeout(checkList, 300);
        } else {
          // timeout on too many tries
          return callback(false);
        }
      });
    }
    setTimeout(checkList, 300);
  }
};

/**
 * Upload method for the AVR109 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadAVR109 = function(eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!self.serialPort) {
    self._setUpSerial();
  }

  self.serialPort.open(function (error) {
    if (error) { return callback(error); }
    self.debug('connected');

    fs.readFile(eggs, function (error, data) {
      if (error) { return callback(error); }

      self.chip.init(self.serialPort, {signature: self.board.signature.toString()}, function (error, flasher) {
        if (error) { return callback(error); }
        self.debug('flashing, please wait...');

        async.series([
          function (callback) {
            flasher.erase(callback);
          },
          function (callback) {
            flasher.program(data.toString(), callback);
          },
          function (callback) {
            flasher.verify(callback);
          },
          function (callback) {
            flasher.fuseCheck(callback);
          }
        ],
        function(err, results) {
          var color = (err ? colors.red : colors.green);

          self.debug(color('flash complete.'));
          return callback(err);
        });
      });
    });
  });
};

/**
 * Finds a list of available USB ports, and matches for the right pid
 * Auto finds the correct port for the chosen Arduino
 *
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._sniffPort = function (callback) {
  var self = this;

  // list all available ports
  Serialport.list(function (err, ports) {
    // iterate through ports
    for (var i = 0; i < ports.length; i++) {
      // iterate through all possible pid's
      for (var j = 0; j < self.board.productId.length; j ++) {
        var pid;
        // are we on windows or unix?
        if (ports[i].productId) {
          pid = ports[i].productId;
        } else if (ports[i].pnpId) {
          pid = '0x' + /PID_\d*/.exec(ports[i].pnpId)[0].substr(4);
        } else {
          pid = '';
        }
        if (pid === self.board.productId[j]) {
          // match! Return the port/path
          return callback(ports[i].comName);
        }
      }
    }
    // didn't find a match :(
    return callback(null);
  });
};

module.exports = Avrgirl_arduino;
<commit_msg>More consistent use of self. vs this. in functions that use it<commit_after>var Serialport = require('serialport');
var intelhex = require('intel-hex');
var Stk500v1 = require('stk500');
var Stk500v2 = require('stk500-v2');
var avr109 = require('chip.avr.avr109');
var async = require('async');
var fs = require('graceful-fs');
var boards = require('./boards');
var childProcess = require('child_process');
var colors = require('colors');

/**
 * Constructor
 *
 * @param {object} options - options for consumer to pass in
 */
var Avrgirl_arduino = function (opts) {
  var opts = opts || {};

  this.options = {
    debug: opts.debug || false,
    board: opts.board || 'uno',
    port: opts.port || ''
  };

  this.debug = this.options.debug ? console.log : function() {};

  this.chip = undefined;

  // get board properties
  this.board = boards[this.options.board];
  if (!this.board) {
    return;
  }

  // assign the correct module to the protocol of the chosen board
  if (this.board.protocol === 'stk500v1') {
    this.chip = new Stk500v1({quiet: true});
  } else if (this.board.protocol === 'stk500v2') {
    this.chip = Stk500v2;
  } else if (this.board.protocol === 'avr109') {
    this.chip = avr109;
  }
};

/**
 * Create new serialport instance for the Arduino board, but do not immediately connect.
 */
Avrgirl_arduino.prototype._setUpSerial = function () {
  this.serialPort = new Serialport.SerialPort(this.options.port, {
    baudRate: this.board.baud,
  }, false);
};

/**
 * Opens and parses a given hex file
 */
Avrgirl_arduino.prototype._parseHex = function (file) {
  var data = fs.readFileSync(file, {encoding: 'utf8'});
  return intelhex.parse(data).data;
};

/**
 * Public method for flashing a hex file to the main program allocation of the Arduino
 *
 * @param {string} file - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype.flash = function (file, callback) {
  var self = this;
  var hex = file;

  // if we don't have a valid board, we cannot continue.
  if (!self.board) {
    return callback(new Error('"' + self.options.board + '" is not a supported board type.'));
  }

  // if we haven't been supplied an explicit port to connect to, auto sniff one.
  if (self.options.port === '') {
    // if this is a pro-mini, we cannot auto-sniff so return an error
    if (self.options.board === 'pro-mini') {
      var error = new Error('you\'re using a pro-mini, please specify the port in your options.');
      return callback(error);
    }

    self._sniffPort(function (port) {
      if (port !== null) {
        self.debug('found ' + self.options.board + ' on port ' + port);
        // found a port, save it
        self.options.port = port;
        // upload hex
        self._upload(hex, callback);
      } else {
        // we didn't find the board
        return callback(new Error('no Arduino found.'));
      }
    });
  } else {
    // we already know the port, so upload
    self._upload(hex, callback);
  }
};

/**
 * Calls the correct upload method, depending on which protocol the Arduino uses
 *
 * @param {string} hex - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._upload = function (hex, callback) {
  var self = this;
  var eggs = hex;
  var cb = callback;

  if (self.board.protocol === 'stk500v1') {
    self._uploadSTK500v1(eggs, cb);
  }

  else if (self.board.protocol === 'stk500v2') {
    self._uploadSTK500v2(eggs, cb);
  }

  else if (self.board.protocol === 'avr109') {
    self._resetAVR109(function (error) {
      if (error) { return cb(error); }
        self.debug('reset complete.');
        self._uploadAVR109(eggs, cb);
    });
  }
};

/**
 * Upload method for the STK500v1 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadSTK500v1 = function (eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!self.serialPort) {
    self._setUpSerial();
  }

  // open connection
  self.serialPort.open(function (error) {
    if (error) { return callback(error); }

    self.debug('connected');

    // open/parse supplied hex file
    var hex = self._parseHex(eggs);

    self.debug('flashing, please wait...');

    // flash
    self.chip.bootload(self.serialPort, hex, self.board, function (err) {
      var color = (err ? colors.red : colors.green);

      self.debug(color('flash complete.'));

      // Always close the serialport
      self.serialPort.close();

      return callback(err);
    });
  });
};

/**
 * Upload method for the STK500v2 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadSTK500v2 = function (eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!self.serialPort) {
    self._setUpSerial();
  }

  // open connection
  self.serialPort.open(function (error) {
    if (error) { return callback(error); }

    // instantiate stk500v2 with newly open serialport
    self.chip = self.chip(self.serialPort);

    self.debug('connected');

    // open/parse supplied hex file
    var hex = self._parseHex(eggs);

    self.debug('flashing, please wait...');

    async.series([
      function (callback) {
        self.chip.sync(5, callback);
      },
      function (callback) {
        self.chip.verifySignature(self.board.signature, callback);
      },
      function (callback) {
        self.chip.enterProgrammingMode(self.board, callback);
      },
      function (callback) {
        self.chip.upload(hex, self.board.pageSize, callback);
      },
      function (callback) {
        self.chip.exitProgrammingMode(callback);
      }
    ],
    function(err, results) {
      var color = (err ? colors.red : colors.green);

      self.debug(color('flash complete.'));

      // Always close the serialport
      self.serialPort.close();

      return callback(err);
    });
  });
};

/**
 * Software resets an Arduino AVR109 bootloaded chip into bootloader mode
 *
 * Note: this method runs a child process, as it is currently difficult to guarantee
 * that a serialport connection has truly closed via node-serialport.
 * Exiting the child process when done ensures we have a true closure,
 * and therefore a completed board reset.
 *
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._resetAVR109 = function (callback) {
  var self = this;
  var resetFile = __dirname + '/lib/leo-reset.js';
  var tries = 0;

  self.debug('resetting board...');

  childProcess.execFile('node', [resetFile, self.options.port], function() {
    tryConnect(function(connected) {
      var status = connected ? null : new Error('could not complete reset.');
      callback(status);
    });
  });

  // here we have to retry the serialport polling,
  // until the chip boots back up to recreate the virtual com port
  function tryConnect (callback) {
    function checkList() {
      Serialport.list(function (error, ports) {
        // iterate through ports looking for the one port to rule them all
        for (var i = 0; i < ports.length; i++) {
          if (ports[i].comName === self.options.port) {
            return callback(true);
          }
        }
        tries += 1;
        if (tries < 4) {
          setTimeout(checkList, 300);
        } else {
          // timeout on too many tries
          return callback(false);
        }
      });
    }
    setTimeout(checkList, 300);
  }
};

/**
 * Upload method for the AVR109 protocol
 *
 * @param {string} eggs - path to hex file for uploading
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._uploadAVR109 = function(eggs, callback) {
  var self = this;

  // do we have a connection instance yet?
  if (!self.serialPort) {
    self._setUpSerial();
  }

  self.serialPort.open(function (error) {
    if (error) { return callback(error); }
    self.debug('connected');

    fs.readFile(eggs, function (error, data) {
      if (error) { return callback(error); }

      self.chip.init(self.serialPort, {signature: self.board.signature.toString()}, function (error, flasher) {
        if (error) { return callback(error); }
        self.debug('flashing, please wait...');

        async.series([
          function (callback) {
            flasher.erase(callback);
          },
          function (callback) {
            flasher.program(data.toString(), callback);
          },
          function (callback) {
            flasher.verify(callback);
          },
          function (callback) {
            flasher.fuseCheck(callback);
          }
        ],
        function(err, results) {
          var color = (err ? colors.red : colors.green);

          self.debug(color('flash complete.'));
          return callback(err);
        });
      });
    });
  });
};

/**
 * Finds a list of available USB ports, and matches for the right pid
 * Auto finds the correct port for the chosen Arduino
 *
 * @param {function} callback - function to run upon completion/error
 */
Avrgirl_arduino.prototype._sniffPort = function (callback) {
  var self = this;

  // list all available ports
  Serialport.list(function (err, ports) {
    // iterate through ports
    for (var i = 0; i < ports.length; i++) {
      // iterate through all possible pid's
      for (var j = 0; j < self.board.productId.length; j ++) {
        var pid;
        // are we on windows or unix?
        if (ports[i].productId) {
          pid = ports[i].productId;
        } else if (ports[i].pnpId) {
          pid = '0x' + /PID_\d*/.exec(ports[i].pnpId)[0].substr(4);
        } else {
          pid = '';
        }
        if (pid === self.board.productId[j]) {
          // match! Return the port/path
          return callback(ports[i].comName);
        }
      }
    }
    // didn't find a match :(
    return callback(null);
  });
};

module.exports = Avrgirl_arduino;
<|endoftext|>


<commit_before>module.exports = function () {
  var transform = require("reify/lib/compiler.js").transform;
  var options = {
    parse: require("reify/lib/parsers/babylon.js").parse
  };

  return {
    visitor: {
      Program: function (path) {
        path.replaceWith(transform(path.node, options));
      }
    }
  };
};
<commit_msg>Pass Babel plugin options through to compiler.transform.<commit_after>module.exports = function () {
  var transform = require("reify/lib/compiler.js").transform;
  var parse = require("reify/lib/parsers/babylon.js").parse;

  return {
    visitor: {
      Program: function (path) {
        transform(path.node, Object.assign({
          parse: parse
        }, this.opts));
      }
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

/**
* Module dependencies.
*/
const mongoose = require('mongoose');
const Game = require('../models/game');
const Score = require('../models/score');
const Team = require('../models/team');
const Player = require('../models/player');

/**
* GET /api/games/
* Get all the games
*/
exports.getGames = function getGames(req, res) {
    Game.find({}, (err, games) => {
        if (err) {
            res.status(403).send('${err}');
            return;
        }
        res.json(games);
    });
}

/**
* POST /api/games/
* Create a new game
*/
exports.postGame = function game(req, res) {
    const newGame = new Game({ teams: [], scores: [] });
    const teamIds = req.body.teams;

    if (teamIds && teamIds.length > 0) {
        var tasksToGo = teamIds.length;

        teamIds.forEach(function (id) {
            if (!mongoose.Types.ObjectId.isValid(id)) {
                if (--tasksToGo === 0) {
                    newGame.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
                return;
            }

            Team.findById(id, (err, team) => {
                if (!err && team) {
                    // if (!newGame.teams.includes(id)) {
                    newGame.teams.push(id);
                    newGame.scores.push(new Score({ team: id }));
                    // }
                }

                if (--tasksToGo === 0) {
                    newGame.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
            });
        });
    } else {
        newGame.save((err, game) => {
            if (err) {
                res.status(403).send(`${err}`);
                return;
            }
            res.status(201).send(game);
        });
    }
}

/**
* PUT /api/games/:gameId/addTeam
* Add teams to a game
*/
exports.addTeam = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('No game found');
            return;
        }

        if (game.started_at) {
            res.status(403).send('Game has started cannot add a team');
            return;
        }

        const teamIds = req.body.teams;

        if (!teamIds && teamIds.length <= 0) {
            res.status(403).send('body empty');
            return;
        }

        var tasksToGo = teamIds.length;

        teamIds.forEach(function (id) {
            if (!mongoose.Types.ObjectId.isValid(id)) {
                if (--tasksToGo === 0) {
                    game.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
                return;
            }

            Team.findById(id, (err, team) => {
                if (!err && team) {
                    // if (!game.teams.includes(id)) {
                    game.teams.push(id);
                    // }
                }

                if (--tasksToGo === 0) {
                    game.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
            });
        });
    });
}

/**
* DELETE /api/games/:gameId
* Delete a game
*/
exports.deleteGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findByIdAndRemove(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('No game found');
            return;
        }

        res.status(201).json(game);
    });
}

/**
* PUT /api/games/:gameId/start
* Start game
*/
exports.startGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('player not found');
            return;
        }

        game.start(() => {
            game.save((err, game) => {
                if (err) {
                    res.status(403).send(`${err}`);
                    return;
                }
                res.status(201).json(game);
            });
        });
    });
}

/**
* PUT /api/games/:gameId/finish
* Finish game
*/
exports.finishGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('player not found');
            return;
        }

        game.finish(() => {
            game.save((err, game) => {
                if (err) {
                    res.status(403).send(`${err}`);
                    return;
                }
                res.status(201).json(game);
            });
        });
    });
}

/**
* PUT /api/games/:gameId/score
* Update score of game
*/
exports.gameScore = function game(req, res) {
    const gameId = req.params.gameId;
    const teamId = req.body.teamId;
    const points = req.body.points;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send('Invalid game id');
        return;
    }

    if (!teamId || !mongoose.Types.ObjectId.isValid(teamId)) {
        res.status(403).send('Invalid team id');
        return;
    }

    if (!points) {
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('Game not found');
            return;
        }

        if (!game.started_at) {
            res.status(403).send('Game not started');
            return;
        }

        if (game.ended_at) {
            res.status(403).send('Game has ended');
            return;
        }

        const scores = game.scores;

        if (scores.length <= 0) {
            res.status(403).send('Game has no scores');
            return;
        }

        var updated = false;

        for (var i = 0; i < scores.length; i++) {
            const score = scores[i];
            if (score.team == teamId) {
                score.points = points;
                updated = true;
                break;
            }
        }

        if (!updated) {
            res.status(403).send('No score found to update');
            return;
        }

        game.save((err, saveGame) => {
            if (err) {
                res.status(403).send(`${err}`);
                return;
            }

            res.status(201).send(saveGame);
        });
    });
}

/**
* GET /api/teams
* List of teams
*/
exports.getTeams = function get(req, res) {
    Team.find({}, (err, teams) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }
        res.status(200).json(teams);
    });
};

/**
* POST /api/teams
* Create a new team
*/
exports.postTeam = function post(req, res) {
    const teamName = req.body.name;
    if (!teamName) {
        res.status(403).send("Request body missing name");
        return;
    }
    const newTeam = new Team({ name: teamName });
    newTeam.save((err) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }
        res.status(201).json(newTeam);
    });
}

/**
* POST /api/teams/:teamId
* Update a team by id
*/
exports.putTeam = function put(req, res, next) {
    const teamId = req.params.teamId;

    if (!mongoose.Types.ObjectId.isValid(teamId)) {
        res.status(403).send('Team id is not valid');
        return;
    }

    Team.findByIdAndUpdate(teamId, { name: req.body.name }, { new: true }, (err, team) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (team) {
            res.status(201).json(team);
        } else {
            res.status(403).send('No team found');
        }
    });
}

/**
* POST /api/teams/:teamId
* Delete a team by id.
*/
exports.deleteTeam = function deleteTeam(req, res) {
    res.status(204).send("Deleted a team");
}
<commit_msg>Add delete to remove team<commit_after>'use strict';

/**
* Module dependencies.
*/
const mongoose = require('mongoose');
const Game = require('../models/game');
const Score = require('../models/score');
const Team = require('../models/team');
const Player = require('../models/player');

/**
* GET /api/games/
* Get all the games
*/
exports.getGames = function getGames(req, res) {
    Game.find({}, (err, games) => {
        if (err) {
            res.status(403).send('${err}');
            return;
        }
        res.json(games);
    });
}

/**
* POST /api/games/
* Create a new game
*/
exports.postGame = function game(req, res) {
    const newGame = new Game({ teams: [], scores: [] });
    const teamIds = req.body.teams;

    if (teamIds && teamIds.length > 0) {
        var tasksToGo = teamIds.length;

        teamIds.forEach(function (id) {
            if (!mongoose.Types.ObjectId.isValid(id)) {
                if (--tasksToGo === 0) {
                    newGame.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
                return;
            }

            Team.findById(id, (err, team) => {
                if (!err && team) {
                    // if (!newGame.teams.includes(id)) {
                    newGame.teams.push(id);
                    newGame.scores.push(new Score({ team: id }));
                    // }
                }

                if (--tasksToGo === 0) {
                    newGame.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
            });
        });
    } else {
        newGame.save((err, game) => {
            if (err) {
                res.status(403).send(`${err}`);
                return;
            }
            res.status(201).send(game);
        });
    }
}

/**
* PUT /api/games/:gameId/addTeam
* Add teams to a game
*/
exports.addTeam = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('No game found');
            return;
        }

        if (game.started_at) {
            res.status(403).send('Game has started cannot add a team');
            return;
        }

        const teamIds = req.body.teams;

        if (!teamIds && teamIds.length <= 0) {
            res.status(403).send('body empty');
            return;
        }

        var tasksToGo = teamIds.length;

        teamIds.forEach(function (id) {
            if (!mongoose.Types.ObjectId.isValid(id)) {
                if (--tasksToGo === 0) {
                    game.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
                return;
            }

            Team.findById(id, (err, team) => {
                if (!err && team) {
                    // if (!game.teams.includes(id)) {
                    game.teams.push(id);
                    // }
                }

                if (--tasksToGo === 0) {
                    game.save((err, game) => {
                        if (err) {
                            res.status(403).send(`${err}`);
                            return;
                        }
                        res.status(201).send(game);
                    });
                }
            });
        });
    });
}

/**
* DELETE /api/games/:gameId
* Delete a game
*/
exports.deleteGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findByIdAndRemove(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('No game found');
            return;
        }

        res.status(201).json(game);
    });
}

/**
* PUT /api/games/:gameId/start
* Start game
*/
exports.startGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('player not found');
            return;
        }

        game.start(() => {
            game.save((err, game) => {
                if (err) {
                    res.status(403).send(`${err}`);
                    return;
                }
                res.status(201).json(game);
            });
        });
    });
}

/**
* PUT /api/games/:gameId/finish
* Finish game
*/
exports.finishGame = function game(req, res) {
    const gameId = req.params.gameId;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send("Invalid player id");
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('player not found');
            return;
        }

        game.finish(() => {
            game.save((err, game) => {
                if (err) {
                    res.status(403).send(`${err}`);
                    return;
                }
                res.status(201).json(game);
            });
        });
    });
}

/**
* PUT /api/games/:gameId/score
* Update score of game
*/
exports.gameScore = function game(req, res) {
    const gameId = req.params.gameId;
    const teamId = req.body.teamId;
    const points = req.body.points;

    if (!mongoose.Types.ObjectId.isValid(gameId)) {
        res.status(403).send('Invalid game id');
        return;
    }

    if (!teamId || !mongoose.Types.ObjectId.isValid(teamId)) {
        res.status(403).send('Invalid team id');
        return;
    }

    if (!points) {
        return;
    }

    Game.findById(gameId, (err, game) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!game) {
            res.status(403).send('Game not found');
            return;
        }

        if (!game.started_at) {
            res.status(403).send('Game not started');
            return;
        }

        if (game.ended_at) {
            res.status(403).send('Game has ended');
            return;
        }

        const scores = game.scores;

        if (scores.length <= 0) {
            res.status(403).send('Game has no scores');
            return;
        }

        var updated = false;

        for (var i = 0; i < scores.length; i++) {
            const score = scores[i];
            if (score.team == teamId) {
                score.points = points;
                updated = true;
                break;
            }
        }

        if (!updated) {
            res.status(403).send('No score found to update');
            return;
        }

        game.save((err, saveGame) => {
            if (err) {
                res.status(403).send(`${err}`);
                return;
            }

            res.status(201).send(saveGame);
        });
    });
}

/**
* GET /api/teams
* List of teams
*/
exports.getTeams = function get(req, res) {
    Team.find({}, (err, teams) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }
        res.status(200).json(teams);
    });
};

/**
* POST /api/teams
* Create a new team
*/
exports.postTeam = function post(req, res) {
    const teamName = req.body.name;
    if (!teamName) {
        res.status(403).send("Request body missing name");
        return;
    }
    const newTeam = new Team({ name: teamName });
    newTeam.save((err) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }
        res.status(201).json(newTeam);
    });
}

/**
* POST /api/teams/:teamId
* Update a team by id
*/
exports.putTeam = function put(req, res, next) {
    const teamId = req.params.teamId;

    if (!mongoose.Types.ObjectId.isValid(teamId)) {
        res.status(403).send('Team id is not valid');
        return;
    }

    Team.findByIdAndUpdate(teamId, { name: req.body.name }, { new: true }, (err, team) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (team) {
            res.status(201).json(team);
        } else {
            res.status(403).send('No team found');
        }
    });
}

/**
* DELETE /api/teams/:teamId
* Delete a team by id
*/
exports.deleteTeam = function deleteTeam(req, res) {
    const teamId = req.params.teamId;

    if (!mongoose.Types.ObjectId.isValid(teamId)) {
        res.status(403).send('Team id is not valid');
        return;
    }

    Team.findByIdAndRemove(teamId, (err, team) => {
        if (err) {
            res.status(403).send(`${err}`);
            return;
        }

        if (!team) {
            res.status(403).send('No team found');
            return;
        }

        res.status(201).json(team);
    });
}

}
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const rollup = require('rollup');
const uglify = require('rollup-plugin-uglify');
const uglifyES = require('uglify-es');
const babel = require('rollup-plugin-babel');
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const del = require('del');
const pkg = require('./package.json');

const resolvePlugin = nodeResolve({
    jsnext: true, // Default: false
    browser: true, // Default: false
    extensions: [
        '.js', '.json'
    ], // Default: ['.js']
    preferBuiltins: false, // Default: true
});

const babelPlugin = babel({
    include: 'src/**',
    exclude: 'node_modules/**',
    presets: [['es2015', {'modules': false}], 'stage-3'],
    runtimeHelpers: true,
    babelrc: false
});

const uglifyPlugin = uglify({}, uglifyES.minify);

const bundles = [
    {
        format: 'umd',
        ext: '.js',
        plugins: [babelPlugin],
        moduleName: 'Ratings'
    }, {
        format: 'umd',
        ext: '.min.js',
        plugins: [babelPlugin, uglifyPlugin],
        moduleName: 'Ratings',
        minify: true
    }
];

let promise = Promise.resolve();

promise = promise.then(() => del(['dist/*']));

for (const config of bundles) {
    let rollupConfig = {
        entry: 'src/main.js',
        plugins: [resolvePlugin, commonjs()].concat(config.plugins),
        onwarn: (warning) => {
            if (warning.code === 'THIS_IS_UNDEFINED') return;
            if (warning.message) console.warn(warning.message);
        }
    };

    let bundleConfig = {
        dest: `dist/ratings${config.ext}`,
        format: config.format,
        sourceMap: !config.minify,
        moduleName: config.moduleName
    };

    promise = promise.then(() => rollup.rollup(rollupConfig).then(bundle => bundle.write(bundleConfig)));

    if (config.ext === '.min.js') {
        let exampleConfig = {
            dest: `example/js/ratings${config.ext}`,
            format: bundleConfig.format,
            sourceMap: false,
            moduleName: bundleConfig.moduleName
        };

        promise = promise.then(() => rollup.rollup(rollupConfig).then(bundle => bundle.write(exampleConfig)));
    }
}

promise = promise.then(() => {
    delete pkg.devDependencies;
    delete pkg.scripts;
    delete pkg.eslintConfig;

    fs.writeFileSync('dist/package.json', JSON.stringify(pkg, null, '  '), 'utf-8');
    fs.writeFileSync('dist/LICENSE.md', fs.readFileSync('LICENSE.md', 'utf-8'), 'utf-8');
});

promise.catch(err => console.error(err.stack)); // eslint-disable-line no-console<commit_msg>Updated build script<commit_after>'use strict';

const fs = require('fs');
const rollup = require('rollup');
const uglify = require('rollup-plugin-uglify');
const uglifyES = require('uglify-es');
const babel = require('rollup-plugin-babel');
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const del = require('del');

const resolvePlugin = nodeResolve({
    jsnext: true, // Default: false
    browser: true, // Default: false
    extensions: [
        '.js', '.json'
    ], // Default: ['.js']
    preferBuiltins: false, // Default: true
});

const babelPlugin = babel({
    include: 'src/**',
    exclude: 'node_modules/**',
    presets: [['es2015', {'modules': false}], 'stage-3'],
    runtimeHelpers: true,
    babelrc: false
});

const uglifyPlugin = uglify({}, uglifyES.minify);

const bundles = [
    {
        format: 'umd',
        ext: '.js',
        plugins: [babelPlugin],
        moduleName: 'Ratings'
    }, {
        format: 'umd',
        ext: '.min.js',
        plugins: [babelPlugin, uglifyPlugin],
        moduleName: 'Ratings',
        minify: true
    }
];

let promise = Promise.resolve();

promise = promise.then(() => del(['dist/*']));

for (const config of bundles) {
    let rollupConfig = {
        entry: 'src/main.js',
        plugins: [resolvePlugin, commonjs()].concat(config.plugins),
        onwarn: (warning) => {
            if (warning.code === 'THIS_IS_UNDEFINED') return;
            if (warning.message) console.warn(warning.message);
        }
    };

    let bundleConfig = {
        dest: `dist/ratings${config.ext}`,
        format: config.format,
        sourceMap: !config.minify,
        moduleName: config.moduleName
    };

    promise = promise.then(() => rollup.rollup(rollupConfig).then(bundle => bundle.write(bundleConfig)));

    if (config.ext === '.min.js') {
        let exampleConfig = {
            dest: `example/js/ratings${config.ext}`,
            format: bundleConfig.format,
            sourceMap: false,
            moduleName: bundleConfig.moduleName
        };

        promise = promise.then(() => rollup.rollup(rollupConfig).then(bundle => bundle.write(exampleConfig)));
    }
}

promise.catch(err => console.error(err.stack)); // eslint-disable-line no-console<|endoftext|>


<commit_before>(function(d3) {

    d3.ui = d3.ui || {};

    d3.ui.FpsMeter = function Histogram(on, style, config) {
        var BINS = 100;
        var _style   = merge({
                "background-color": 'black',
                display: "inline-block",
                margin: "0 0 0 6px"
            }, style),
            hist     = d3.select(on).append("div")
                .style(_style)
                .attr("id", "histogram")
                .append("svg")
                .style(merge({display: "inline-block", overflow: "visible"}, style))
                .attr(config),
            fill     = hist.append("rect")
                .attr({
                    height: config.height,
                    width: config.width,
                    fill: "url(#xColor)"
                }),
            plot     = hist.append("g")
            /*.attr(transplot(config.height))*/,
            xAxis    = d3.svg.axis()
                .orient("bottom")
                .tickFormat("")
                .ticks(3)
                .tickSize(config.tickSize || 3),
            xG       = hist.append("g")
                .attr("id", "x-axis-g")
                .attr({
                    "fill": "none", stroke: 'rgba(207,203,196,0.3)',
                    "transform": "translate(0," + config.height + ")"
                }),

            defs     = hist.append("defs"),
            gradient = defs.append("linearGradient")
                .attr({id: "xColor", x1: "0%", y1: "0%", x2: "100%", y2: "0%"});
        gradient.append("stop")
            .attr({"offset": "0%", "stop-color": "red", "stop-opacity": "0.5"});
        gradient.append("stop")
            .attr({"offset": "50%", "stop-color": "orange", "stop-opacity": "0.5"});
        gradient.append("stop")
            .attr({"offset": "100%", "stop-color": "green", "stop-opacity": "0.5"});

        update(d3.range(BINS));

        function update(data) {
            if(!data || !data.length) return;
            var x    = d3.scale.linear()
                    .domain(config.domain || [0, d3.max(data, config.values)])
                    .range([0, config.width]),
                h    = makeHist(data, config.values, x),
                y    = d3.scale.linear()
                    .domain([0, d3.max(h, function(d) {return d.y})])
                    .range([config.height, 0]),
                bars = plot.selectAll(".bar").data(h);

            bars.enter().append("rect")
                .attr("class", "bar")
                .style({fill: _style["background-color"], stroke: "none"});

            bars.exit().remove();

            bars.attr("height", function(d) {
                return y(d.y)
            })
                .attr("width", function(d) {
                    return x(d.dx)
                })
                .attr("x", function(d) {
                    return x(d.x)
                });
            xAxis.scale(x);
            xG.call(xAxis);
            xG.selectAll(".domain").attr({"fill": "url(#xColor)"})
        }

        return update;

        function makeHist(data, values, x) {
            var bins = d3.scale.ordinal()
                .domain(d3.range(BINS))
                .rangePoints(x.domain())
                .range();
            return d3.layout.histogram()
                .bins(bins)
                .value(values)(data)
        }

        function merge(source, target) {
            for(var p in source) if(target && !target.hasOwnProperty(p)) target[p] = source[p];
            return target;
        }
    }

})(d3)
<commit_msg>plot - add transplot to the plot area - put the gradient on the parent element and use userSpaceOnUse<commit_after>(function(d3) {

    d3.ui = d3.ui || {};

    d3.ui.FpsMeter = function Histogram(on, style, config) {
        var BINS = 100;
        var _style   = merge({
                "background-color": 'rgba(207,203,196,0.1)',
                display: "inline-block",
                margin: "0 0 0 6px"
            }, style),
            hist     = d3.select(on).append("div")
                .style(_style)
                .attr("id", "histogram")
                .append("svg")
                .style({display: "inline-block", overflow: "visible", margin: 0})
                .attr(merge({fill: "url(#xColor)"}, config)),
            plot     = hist.append("g")
                .attr(transplot(config.height))
                .attr({"fill": "url(#xColor)"}),
            xAxis    = d3.svg.axis()
                .orient("bottom")
                .tickFormat("")
                .ticks(3)
                .tickSize(config.tickSize || 3),
            xG       = hist.append("g")
                .attr("id", "x-axis-g")
                .attr({
                    "fill": "none", stroke: 'rgba(207,203,196,0.3)',
                    "transform": "translate(0," + config.height + ")"
                }),

            defs     = hist.append("defs"),
            gradient = defs.append("linearGradient")
                .attr({
                    id: "xColor", x1: "0%", y1: "0%", x2: "100%", y2: "0%",
                    gradientUnits: "userSpaceOnUse"
                });
        gradient.append("stop")
            .attr({"offset": "0%", "stop-color": "red", "stop-opacity": "0.5"});
        gradient.append("stop")
            .attr({"offset": "50%", "stop-color": "orange", "stop-opacity": "0.5"});
        gradient.append("stop")
            .attr({"offset": "100%", "stop-color": "green", "stop-opacity": "0.5"});

        update(d3.range(BINS));

        function update(data) {
            if(!data || !data.length) return;
            var x    = d3.scale.linear()
                    .domain(config.domain || [0, d3.max(data, config.values)])
                    .range([0, config.width]),
                h    = makeHist(data, config.values, x),
                y    = d3.scale.linear()
                    .domain([0, d3.max(h, function(d) {return d.y})])
                    .range([0, config.height]),
                bars = plot.selectAll(".bar").data(h);

            bars.enter().append("rect")
                .attr("class", "bar")
                .attr({opactity: 1});

            bars.exit().remove();

            bars.attr("height", function(d) {
                return y(d.y)
            })
                .attr("width", function(d) {
                    return x(d.dx)
                })
                .attr("x", function(d) {
                    return x(d.x)
                });
            xAxis.scale(x);
            xG.call(xAxis);
            xG.selectAll(".domain")
                .attr({"fill": "url(#xColor)"})
        }

        return update;

        function makeHist(data, values, x) {
            var bins = d3.scale.ordinal()
                .domain(d3.range(BINS))
                .rangePoints(x.domain())
                .range();
            return d3.layout.histogram()
                .bins(bins)
                .value(values)(data)
        }

        function merge(source, target) {
            for(var p in source) if(target && !target.hasOwnProperty(p)) target[p] = source[p];
            return target;
        }
    }

})(d3)
<|endoftext|>


<commit_before>'use stict';

var chalk = require('chalk');
var execSync = require('child_process').execSync;
var fs = require('fs');
var readPkg = require('read-pkg-up').sync;
var stripEof = require('strip-eof');

var Helpers = {
	capitalize: function (str) {
		return str.charAt(0).toUpperCase() + str.slice(1);
	},

	checkDirectory: function (dir) {
		if (!fs.existsSync(dir)) {
			this.error('no such file or directory: ' + dir, 1);
		}

		if (!fs.statSync(dir).isDirectory()) {
			this.error('not a directory: ' + dir, 1);
		}
	},

	checkGitExec: function () {
		this.exec('git --version', {stdio: 'ignore'}, 'Missing required executable: git');
	},

	checkGitRepo: function () {
		this.log('Checking if "' + chalk.cyan(process.cwd()) + '" is a Git repository ...');
		this.exec('git rev-parse', {stdio: ['ignore', 'ignore', 'pipe']});
	},

	error: function (msg, code) {
		var err = chalk.red.bold;
		console.log(err(msg));
		console.log(err('Aborting.'));
		process.exit(code);
	},

	exec: function (cmd, opts, msg) {
		try {
			var rs = execSync(cmd, opts);
			return rs && stripEof(rs).toString();
		} catch (e) {
			this.error(msg || stripEof(e.stderr).toString().split(/\r?\n/)[0], e.status || 1);
		}
	},

	format: function (commits, component) {
		var str = component ? '## ' + component + '\n' : '';
		return str + commits.map(function (commit) {
			return '  * ' + commit;
		}).join('\n') + '\n\n';
	},

	getLog: function (commitish) {
		var rs;
		var tag;
		if (!commitish) {
			tag = this.getTag();
			commitish = tag && tag + '..HEAD';
		}

		this.log('Getting the list of commits...');

		rs = this.exec('git log ' + commitish + ' --no-merges --pretty=format:"%s"',
					{stdio: ['ignore', 'pipe', 'pipe']});
		if (!rs) {
			this.error('No commits found', 1);
		}

		return rs;
	},

	getTag: function () {
		var commithash;
		try {
			commithash = execSync('git rev-list --tags --max-count=1',
									{stdio: ['ignore', 'pipe', 'ignore']});
		} catch (e) {
			return '';
		}

		return this.exec('git describe --tags ' + commithash);
	},

	getVersion: function () {
		var pkg = readPkg().pkg;
		var version = pkg && pkg.version || 'x.x.x';
		return version.replace('-pre', '');
	},

	log: function (msg) {
		if (!this.verbose) {
			return;
		}

		console.log(msg || '');
	},

	section: function (sect) {
		this.log();
		this.log(chalk.blue.bold('→ ') + chalk.magenta(sect));
		this.log();
	},

	today: function () {
		return new Date().toISOString().replace(/T.+/, '');
	}
};

module.exports = Helpers;
<commit_msg>minor tweaks<commit_after>'use stict';

var chalk = require('chalk');
var execSync = require('child_process').execSync;
var fs = require('fs');
var readPkg = require('read-pkg-up').sync;
var stripEof = require('strip-eof');

var Helpers = {
	capitalize: function (str) {
		return str.charAt(0).toUpperCase() + str.slice(1);
	},

	checkDirectory: function (dir) {
		if (!fs.existsSync(dir)) {
			this.error('no such file or directory: ' + dir, 1);
		}

		if (!fs.statSync(dir).isDirectory()) {
			this.error('not a directory: ' + dir, 1);
		}
	},

	checkGitExec: function () {
		this.exec('git --version', {stdio: 'ignore'}, 'Missing required executable: git');
	},

	checkGitRepo: function () {
		this.log('Checking if "' + chalk.cyan(process.cwd()) + '" is a Git repository ...');
		this.exec('git rev-parse', {stdio: ['ignore', 'ignore', 'pipe']});
	},

	error: function (msg, code) {
		var err = chalk.red.bold;
		console.log(err(msg));
		console.log(err('Aborting.'));
		process.exit(code);
	},

	exec: function (cmd, opts, msg) {
		try {
			var rs = execSync(cmd, opts);
			return rs && stripEof(rs).toString();
		} catch (e) {
			this.error(msg || stripEof(e.stderr).toString().split(/\r?\n/)[0], e.status || 1);
		}
	},

	format: function (commits, component) {
		var str = component ? '## ' + component + '\n' : '';
		return str + commits.map(function (commit) {
			return '  * ' + commit;
		}).join('\n') + '\n\n';
	},

	getLog: function (commitish) {
		var rs;
		var tag;
		if (!commitish) {
			tag = this.getTag();
			commitish = tag && tag + '..HEAD';
		}

		this.log('Getting the list of commits...');

		rs = this.exec('git log ' + commitish + ' --no-merges --pretty=format:"%s"',
					{stdio: ['ignore', 'pipe', 'pipe']});
		if (!rs) {
			this.error('No commits found', 1);
		}

		return rs;
	},

	getTag: function () {
		var commithash;
		try {
			commithash = execSync('git rev-list --tags --max-count=1',
						{stdio: ['ignore', 'pipe', 'ignore']});
		} catch (e) {
			return '';
		}

		return this.exec('git describe --tags ' + commithash);
	},

	getVersion: function () {
		var pkg = readPkg().pkg;
		var version = pkg && pkg.version || 'x.x.x';
		return version.replace('-pre', '');
	},

	log: function (msg) {
		if (!this.verbose) {
			return;
		}

		console.log(msg || '');
	},

	section: function (sect) {
		this.log();
		this.log(chalk.blue.bold('→ ') + chalk.magenta(sect));
		this.log();
	},

	today: function () {
		return new Date().toISOString().replace(/T.+/, '');
	}
};

module.exports = Helpers;
<|endoftext|>


<commit_before>var util = require('util');
var events = require('events');

var factory;
Module.exports = function(options) {
  if(!factory) {
    factory = new DFGFactory(options);
  }
  return factory;
}

function DFGFactory(options) {
  events.EventEmitter.call(this);
  if(!options) {
    options.useFs = true;
    options.rootPath = './cfg/';
  }
  this._options = options;
  this._types = {};
  this._loadData();
};

util.inherits(DFGFactory,events.EventEmitter);

DFGFactory.prototype._loadData = function() {
  var self = this;
  if(this._options.useFs) {
    loadFsData(this._options.rootPath)(function(data) {
      self._data = data;
      self.emit('ready',self);
    });
  }
  else throw "Not implemented";
}

var loadFsData = function(rootPath) {
//hmm. this is async, but api is currently not async...........
//promises? callback? event? have to think.
};

DFGFactory.prototype.createType = function(typeName, overrideScheme) {
  if(this._types[typeName]) throw "Duplicate declaration of type " + typeName;
  if(!this._data[typeName]) throw "No source material for type " + typeName;

  this._types[typeName] = new DFGType(overrideScheme,this._data[typeName]);
};

DFGFactory.prototype.getType = function(typeName, context) {
  if(!this._types[typeName]) throw "Unknown type " + typeName;
  return this._types[typeName](context);
};

DFGFactory.prototype.typeGetter = function(typeName) { //partial!
  var self = this;
  return function(context) {
    return self.getType(typeName,context);
  };
};

function DFGType(overrideScheme) {

};
<commit_msg>dev!!!<commit_after>var util = require('util');
var events = require('events');
var loadJsons = require('./loadJsons');

var factory;
module.exports = function(options) {
  if(!factory) {
    factory = new DFGFactory(options);
  }
  return factory;
}

function DFGFactory(options) {
  events.EventEmitter.call(this);
  if(!options) {
    options.useFs = true;
    options.rootPath = './cfg/';
  }
  this._options = options;
  this._types = {};
  this.ready = false;
  this._loadData();
};

util.inherits(DFGFactory,events.EventEmitter);

DFGFactory.prototype._loadData = function() {
  var self = this;
  if(this._options.useFs) {
    loadFsData(this._options.rootPath)(function(data) {
      self._data = data;
      self.ready = true;
      self.emit('ready',self);
    });
  }
  else throw "Not implemented";
}

var loadFsData = function(rootPath, cb) {
  loadJsons(rootPath)(cb);
};

DFGFactory.prototype.createType = function(typeName, overrideScheme) {
  if(this._types[typeName]) throw "Duplicate declaration of type " + typeName;
  if(!this._data[typeName]) throw "No source material for type " + typeName;

  this._types[typeName] = new DFGType(overrideScheme,this._data[typeName]);
};

DFGFactory.prototype.getType = function(typeName, context) {
  if(!this._types[typeName]) throw "Unknown type " + typeName;
  return this._types[typeName](context);
};

DFGFactory.prototype.typeGetter = function(typeName) { //partial!
  var self = this;
  return function(context) {
    return self.getType(typeName,context);
  };
};

function DFGType(overrideScheme) {

};
<|endoftext|>


<commit_before>import React from 'react'
import styled, { keyframes } from 'styled-components'
import PropTypes from 'prop-types'
import { Card } from '../../..'
import Flexbox from '../../layout/Flexbox2/Flexbox2'
import IconColorful from '../../content/IconColorful/IconColorful'
import Image from '../../content/Image/Image'
import Spacing from '../../layout/Spacing/Spacing'
import Text from '../../content/Text/Text'
import Title from '../../content/Title/Title'

const textColor = '#4a4a4a'

const placeHolderShimmer = keyframes`
  0% { -webkit-transform: translateX(-180%) }
  100% { -webkit-transform: translateX(70%) }
`

const BackgroundMasker = styled.div`
  background: #fff;
  position: absolute;
`

const AnimatedBackground = styled.div`
  animation-name: ${placeHolderShimmer};
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  background: #f6f7f8;
  background: linear-gradient(to right, transparent 30%, #dddddd 70%, transparent 100%);
  height: 300px;
  position: relative;
`

const PanelLoading = styled(({ className, minHeight, title }) => (
  <Card title={title} minHeight={minHeight}>

      <AnimatedBackground />
      <BackgroundMasker style={{ top: 182, left: 0, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 185, bottom: 0, width: 18 }} />
      <BackgroundMasker style={{ top: 200, left: 185, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 218, left: 0, right: 0, height: 12 }} />
      <BackgroundMasker style={{ top: 230, left: 254, right: 0, height: 14 }} />
      <BackgroundMasker style={{ top: 244, left: 0, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 262, left: 120, right: 0, height: 10 }} />
      <BackgroundMasker style={{ top: 272, left: 0, right: 0, height: 28 }} />

  </Card>
))`
  background: #eeeeee;
  border-color: #e5e6e9 #dfe0e4 #d0d1d5;
  min-height: 300px;
  position: relative;
  overflow: hidden;
`

const DefaultImage = styled(IconColorful, ({ className }) => (

    <IconColorful name='community' size={130} />

))`
  display: flex;
  justify-content: center;
  align-items: center;
  height: 185px;
  background-color: #424242;
`

const Panel = ({
  loading,
  image,
  minHeight,
  sectionTitle,
  author,
  title,
  description,
  onClick
}) => loading ? <PanelLoading title={sectionTitle} minHeight={minHeight} /> : (
  <Card title={sectionTitle} minHeight={minHeight} onClick={onClick}>
    <Flexbox vertical spacing='between'>

        {image
          ? <Image src={image} height={185} />
          : <DefaultImage />
        }

        <Spacing padding={{ x: 16, top: 14 }}>
          <Title.H4>{title}</Title.H4>
          <Spacing margin={{ y: 8 }}>
            <Text fontSize={16} lineHeight={1.31} color={textColor}>
              {description}
            </Text>
          </Spacing>
        </Spacing>

      <Spacing padding={{ x: 16, bottom: 14 }}>
        <Text fontSize={13} lineHeight={1.85} color={textColor}>
          {author}
        </Text>
      </Spacing>
    </Flexbox>
  </Card>
)

const { bool, string, number } = PropTypes

Panel.propTypes = {
  loading: bool,
  sectionTitle: string,
  minHeight: number,
  image: string,
  title: string.isRequired,
  description: string.isRequired,
  author: string.isRequired
}

Panel.defaultProps = {
  loading: false,
  minHeight: 320
}

Panel.displayName = 'Panel'

/** @component */
export default Panel
<commit_msg>chore(styleguide): remove -webkit Panel<commit_after>import React from 'react'
import styled, { keyframes } from 'styled-components'
import PropTypes from 'prop-types'
import { Card } from '../../..'
import Flexbox from '../../layout/Flexbox2/Flexbox2'
import IconColorful from '../../content/IconColorful/IconColorful'
import Image from '../../content/Image/Image'
import Spacing from '../../layout/Spacing/Spacing'
import Text from '../../content/Text/Text'
import Title from '../../content/Title/Title'

const textColor = '#4a4a4a'

const placeHolderShimmer = keyframes`
  0% { transform: translateX(-180%) }
  100% { transform: translateX(70%) }
`

const BackgroundMasker = styled.div`
  background: #fff;
  position: absolute;
`

const AnimatedBackground = styled.div`
  animation-name: ${placeHolderShimmer};
  animation-duration: 1s;
  animation-fill-mode: forwards;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  background: #f6f7f8;
  background: linear-gradient(to right, transparent 30%, #dddddd 70%, transparent 100%);
  height: 300px;
  position: relative;
`

const PanelLoading = styled(({ className, minHeight, title }) => (
  <Card title={title} minHeight={minHeight}>

      <AnimatedBackground />
      <BackgroundMasker style={{ top: 182, left: 0, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 185, bottom: 0, width: 18 }} />
      <BackgroundMasker style={{ top: 200, left: 185, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 218, left: 0, right: 0, height: 12 }} />
      <BackgroundMasker style={{ top: 230, left: 254, right: 0, height: 14 }} />
      <BackgroundMasker style={{ top: 244, left: 0, right: 0, height: 18 }} />
      <BackgroundMasker style={{ top: 262, left: 120, right: 0, height: 10 }} />
      <BackgroundMasker style={{ top: 272, left: 0, right: 0, height: 28 }} />

  </Card>
))`
  background: #eeeeee;
  border-color: #e5e6e9 #dfe0e4 #d0d1d5;
  min-height: 300px;
  position: relative;
  overflow: hidden;
`

const DefaultImage = styled(IconColorful, ({ className }) => (

    <IconColorful name='community' size={130} />

))`
  display: flex;
  justify-content: center;
  align-items: center;
  height: 185px;
  background-color: #424242;
`

const Panel = ({
  loading,
  image,
  minHeight,
  sectionTitle,
  author,
  title,
  description,
  onClick
}) => loading ? <PanelLoading title={sectionTitle} minHeight={minHeight} /> : (
  <Card title={sectionTitle} minHeight={minHeight} onClick={onClick}>
    <Flexbox vertical spacing='between'>

        {image
          ? <Image src={image} height={185} />
          : <DefaultImage />
        }

        <Spacing padding={{ x: 16, top: 14 }}>
          <Title.H4>{title}</Title.H4>
          <Spacing margin={{ y: 8 }}>
            <Text fontSize={16} lineHeight={1.31} color={textColor}>
              {description}
            </Text>
          </Spacing>
        </Spacing>

      <Spacing padding={{ x: 16, bottom: 14 }}>
        <Text fontSize={13} lineHeight={1.85} color={textColor}>
          {author}
        </Text>
      </Spacing>
    </Flexbox>
  </Card>
)

const { bool, string, number } = PropTypes

Panel.propTypes = {
  loading: bool,
  sectionTitle: string,
  minHeight: number,
  image: string,
  title: string.isRequired,
  description: string.isRequired,
  author: string.isRequired
}

Panel.defaultProps = {
  loading: false,
  minHeight: 320
}

Panel.displayName = 'Panel'

/** @component */
export default Panel
<|endoftext|>