<commit_before>/* eslint
    no-console: 0,
    no-param-reassign: 0,
    no-use-before-define: ["error", { "variables": false }],
    no-return-assign: 0,
    react/no-string-refs: 0,
    react/sort-comp: 0
*/

import PropTypes from 'prop-types';
import React from 'react';

import { FlatList, View, StyleSheet, Platform } from 'react-native';

import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.PureComponent {

  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderHeaderWrapper = this.renderHeaderWrapper.bind(this);
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return <LoadEarlier {...loadEarlierProps} />;
    }
    return null;
  }

  scrollTo(options) {
    if (this.flatListRef) {
      this.flatListRef.scrollToOffset(options);
    }
  }

  renderRow({ item, index }) {
    if (!item._id && item._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(item));
    }
    if (!item.user) {
      if (!item.system) {
        console.warn('GiftedChat: `user` is missing for message', JSON.stringify(item));
      }
      item.user = {};
    }
    const { messages, ...restProps } = this.props;
    const previousMessage = messages[index + 1] || {};
    const nextMessage = messages[index - 1] || {};

    const messageProps = {
      ...restProps,
      key: item._id,
      currentMessage: item,
      previousMessage,
      nextMessage,
      position: item.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps} />;
  }

  renderHeaderWrapper() {
    return <View style={styles.headerWrapper}>{this.renderLoadEarlier()}</View>;
  }

  render() {
    if (this.props.messages.length === 0) {
      return <View style={styles.container} />;
    }
    return (
      <View style={styles.container}>
        <FlatList
          ref={(ref) => (this.flatListRef = ref)}
          keyExtractor={(item) => item._id}
          enableEmptySections
          automaticallyAdjustContentInsets={false}
          removeClippedSubviews={Platform.OS === 'android'}
          inverted={this.props.inverted}
          {...this.props.listViewProps}
          data={this.props.messages}
          style={styles.listStyle}
          contentContainerStyle={styles.contentContainerStyle}
          renderItem={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          {...this.props.invertibleScrollViewProps}
          ListFooterComponent={this.renderHeaderWrapper}
        />
      </View>
    );
  }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainerStyle: {
    justifyContent: 'flex-end',
  },
  headerWrapper: {
    flex: 1,
  },
  listStyle: {
    flex: 1,
  },
});

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {},
  inverted: true,
  loadEarlier: false,
  listViewProps: {},
  invertibleScrollViewProps: {}, // TODO: support or not?
};

MessageContainer.propTypes = {
  messages: PropTypes.arrayOf(PropTypes.object),
  user: PropTypes.object,
  renderFooter: PropTypes.func,
  renderMessage: PropTypes.func,
  renderLoadEarlier: PropTypes.func,
  onLoadEarlier: PropTypes.func,
  listViewProps: PropTypes.object,
  inverted: PropTypes.bool,
  loadEarlier: PropTypes.bool,
  invertibleScrollViewProps: PropTypes.object, // TODO: support or not?
};
<commit_msg>Remove prop removeClippedSubviews on Flatlist (#897)<commit_after>/* eslint
    no-console: 0,
    no-param-reassign: 0,
    no-use-before-define: ["error", { "variables": false }],
    no-return-assign: 0,
    react/no-string-refs: 0,
    react/sort-comp: 0
*/

import PropTypes from 'prop-types';
import React from 'react';

import { FlatList, View, StyleSheet } from 'react-native';

import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.PureComponent {

  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderHeaderWrapper = this.renderHeaderWrapper.bind(this);
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return <LoadEarlier {...loadEarlierProps} />;
    }
    return null;
  }

  scrollTo(options) {
    if (this.flatListRef) {
      this.flatListRef.scrollToOffset(options);
    }
  }

  renderRow({ item, index }) {
    if (!item._id && item._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(item));
    }
    if (!item.user) {
      if (!item.system) {
        console.warn('GiftedChat: `user` is missing for message', JSON.stringify(item));
      }
      item.user = {};
    }
    const { messages, ...restProps } = this.props;
    const previousMessage = messages[index + 1] || {};
    const nextMessage = messages[index - 1] || {};

    const messageProps = {
      ...restProps,
      key: item._id,
      currentMessage: item,
      previousMessage,
      nextMessage,
      position: item.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps} />;
  }

  renderHeaderWrapper() {
    return <View style={styles.headerWrapper}>{this.renderLoadEarlier()}</View>;
  }

  render() {
    if (this.props.messages.length === 0) {
      return <View style={styles.container} />;
    }
    return (
      <View style={styles.container}>
        <FlatList
          ref={(ref) => (this.flatListRef = ref)}
          keyExtractor={(item) => item._id}
          enableEmptySections
          automaticallyAdjustContentInsets={false}
          inverted={this.props.inverted}
          {...this.props.listViewProps}
          data={this.props.messages}
          style={styles.listStyle}
          contentContainerStyle={styles.contentContainerStyle}
          renderItem={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          {...this.props.invertibleScrollViewProps}
          ListFooterComponent={this.renderHeaderWrapper}
        />
      </View>
    );
  }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  contentContainerStyle: {
    justifyContent: 'flex-end',
  },
  headerWrapper: {
    flex: 1,
  },
  listStyle: {
    flex: 1,
  },
});

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {},
  inverted: true,
  loadEarlier: false,
  listViewProps: {},
  invertibleScrollViewProps: {}, // TODO: support or not?
};

MessageContainer.propTypes = {
  messages: PropTypes.arrayOf(PropTypes.object),
  user: PropTypes.object,
  renderFooter: PropTypes.func,
  renderMessage: PropTypes.func,
  renderLoadEarlier: PropTypes.func,
  onLoadEarlier: PropTypes.func,
  listViewProps: PropTypes.object,
  inverted: PropTypes.bool,
  loadEarlier: PropTypes.bool,
  invertibleScrollViewProps: PropTypes.object, // TODO: support or not?
};
<|endoftext|>


<commit_before>// qgr-cntrl-pills

define(function (require) {
  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var Handlebars = require('handlebars');
  var pills_tmpl = require('text!tmpl/pills.html');

  var PillChoice = Backbone.Model.extend({
    // Has attr choice_val

    initialize: function() {
      this.col = options.col;
    }

    get_subtree: function() {
      return {
        eq: [
          this.col,
          c.get('choice_val');
        ]
      };
    }

  });

  var PillChoiceView = Backbone.View.extend({
    // Represent an individual checkbox with a view.

    tagName: 'label',

    class: 'checkbox',

    tmpl: Handlebars.compile(pills_tmpl),

    events: {
      'click li': 'set_choice',
      'click': 'clicked',
    },

    initialize: function(options) {
      // Initialize with a choice model in the options hash.
      _.bindAll(this, 'render', 'set_choice')
      this.choices = this.options.choices;
    },

    render: function() {
      var render_content = this.tmpl({
        choice: this.choices
      })
      this.$el.html(render_content);
      return this
    },

    set_choice: function(e) {
      console.log(e);
    },

    clicked: function(e) {
      e.stopImmediatePropagation();
    }

  });

  // Return exports.
  return {
    PillChoice: PillChoice,
    PillChoiceView: PillChoiceView,
  };

});

<commit_msg>Remove tagName and class from PillChoiceView<commit_after>// qgr-cntrl-pills

define(function (require) {
  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var Handlebars = require('handlebars');
  var pills_tmpl = require('text!tmpl/pills.html');

  var PillChoice = Backbone.Model.extend({
    // Has attr choice_val

    initialize: function() {
      this.col = options.col;
    },

    get_subtree: function() {
      return {
        eq: [
          this.col,
          c.get('choice_val');
        ]
      };
    }

  });

  var PillChoiceView = Backbone.View.extend({
    // Represent an individual checkbox with a view.

    tmpl: Handlebars.compile(pills_tmpl),

    events: {
      'click li': 'set_choice',
      'click': 'clicked',
    },

    initialize: function(options) {
      // Initialize with a choice model in the options hash.
      _.bindAll(this, 'render', 'set_choice')
      this.choices = this.options.choices;
    },

    render: function() {
      var render_content = this.tmpl({
        choice: this.choices
      })
      this.$el.html(render_content);
      return this
    },

    set_choice: function(e) {
      console.log(e);
    },

    clicked: function(e) {
      e.stopImmediatePropagation();
    }

  });

  // Return exports.
  return {
    PillChoice: PillChoice,
    PillChoiceView: PillChoiceView,
  };

});

<|endoftext|>


<commit_before>'use strict';

angular.module('campaign').controller('userCampaignsCtrl', ['$scope', 'backendService', 'toaster', '$location', 'subledgerServices', 'Authentication', '$stateParams', 'lodash', 'credentials', '$state',
    function($scope, backendService, toaster, $location, subledgerServices, Authentication, $stateParams, lodash, credentials, $state) {

        $scope.myCampaigns = [];
        $scope.systemBalance = {};
        $scope.balance = {};
        $scope.journal = [];
        $scope.myJournal = [];
        $scope.authentication = Authentication;
        Authentication.requireLogin($state);
        //checks if user is an admin
        $scope.isAdmin = Authentication.hasRole('admin');
        $scope.isBanker = Authentication.hasRole('banker');
        $scope.query = $scope.authentication.user.displayName;
        var cred = credentials.data;
        subledgerServices.setCredentials(cred);

        $scope.isDistributor = Authentication.hasRole('distributor');

        //uses the Currently signed-in id to get the user id.
        var userid = $scope.authentication.user._id;

        backendService.getUserCampaigns(userid)
            .success(function(myCampaigns) {
                $scope.myCampaigns = myCampaigns;
            })
            .error(function(error, status, header, config) {
                //not cool to redirect the user if any error occured, should be improved by
                //checking for the exact error act base on the error
                $location.path('/');
            });

        $scope.getCurrentBalance = function(account, destination) {
            subledgerServices.getBalance(account, function(response) {
                destination.amount = response;
                $scope.$digest();
            });
        };
        $scope.getCurrentBalance(cred.bank_id, $scope.systemBalance);
        $scope.getCurrentBalance($scope.authentication.user.account_id, $scope.balance);

        //GET UNIQUE USER JOURNAL REPORTS
        $scope.getJournals = function(account, cb) {
            subledgerServices.getJournals(account, function(response) {
                response = response.posted_lines;
                cb(response);
            });
        };

        /*
            This is the method that fetches the  transaction journal for the banker using the bank id
            If the Authenticated User plays Both Role of A banker and a Distributor the Data will have the same values
        */
        $scope.getJournals(cred.bank_id, function(response) {
            $scope.journal = response;
            $scope.$digest();
        });

        //This is the method that loads the Transaction journal for the Authenticated User.
        $scope.getJournals($scope.authentication.user.account_id, function(response) {
            $scope.myJournal = response;
            $scope.$digest();
        });
        // function to click the show more button on getMoreCampaigns page
        $scope.limit = 4;
        $scope.increment = function() {
            var campaignLength = $scope.myCampaigns.length;
            $scope.limit = campaignLength;
        };

        $scope.decrement = function() {
            $scope.limit = 4;
        };
    }
]);
<commit_msg>added amount raised for campaign<commit_after>'use strict';

angular.module('campaign').controller('userCampaignsCtrl', ['$scope', 'backendService', 'toaster', '$location', 'subledgerServices', 'Authentication', '$stateParams', 'lodash', 'credentials', '$state',
  function($scope, backendService, toaster, $location, subledgerServices, Authentication, $stateParams, lodash, credentials, $state) {

    $scope.myCampaigns = [];
    $scope.systemBalance = {};
    $scope.balance = {};
    $scope.journal = [];
    $scope.myJournal = [];
    $scope.authentication = Authentication;
    Authentication.requireLogin($state);
    //checks if user is an admin
    $scope.isAdmin = Authentication.hasRole('admin');
    $scope.isBanker = Authentication.hasRole('banker');
    $scope.query = $scope.authentication.user.displayName;
    var cred = credentials.data;
    subledgerServices.setCredentials(cred);

    $scope.isDistributor = Authentication.hasRole('distributor');

    //uses the Currently signed-in id to get the user id.
    var userid = $scope.authentication.user._id;

    backendService.getUserCampaigns(userid)
      .success(function(data) {
        $scope.myCampaigns = data;
        for (var i = 0; i < $scope.myCampaigns.length; i++) {
          var accountNo = data[i].account_id;

          $scope.getCampaignBalance(accountNo, $scope.myCampaigns[i]);
        }
        // $scope.myCampaigns =
      })
      .error(function(error, status, header, config) {
        //not cool to redirect the user if any error occured, should be improved by
        //checking for the exact error act base on the error
        $location.path('/');
      });

    $scope.getCampaignBalance = function(account, destination) {
      subledgerServices.getBalance(account, function(response) {
        destination.raised = response;

        $scope.$digest();
      });
    };

    $scope.getCurrentBalance = function(account, destination) {
      subledgerServices.getBalance(account, function(response) {
        destination.amount = response;
        $scope.$digest();
      });
    };

    $scope.getCurrentBalance(cred.bank_id, $scope.systemBalance);
    $scope.getCurrentBalance($scope.authentication.user.account_id, $scope.balance);

    //GET UNIQUE USER JOURNAL REPORTS
    $scope.getJournals = function(account, cb) {
      subledgerServices.getJournals(account, function(response) {
        response = response.posted_lines;
        cb(response);
      });
    };

    /*
        This is the method that fetches the  transaction journal for the banker using the bank id
        If the Authenticated User plays Both Role of A banker and a Distributor the Data will have the same values
    */
    $scope.getJournals(cred.bank_id, function(response) {
      $scope.journal = response;
      $scope.$digest();
    });

    //This is the method that loads the Transaction journal for the Authenticated User.
    $scope.getJournals($scope.authentication.user.account_id, function(response) {
      $scope.myJournal = response;
      $scope.$digest();
    });
    // function to click the show more button on getMoreCampaigns page
    $scope.limit = 4;
    $scope.increment = function() {
      var campaignLength = $scope.myCampaigns.length;
      $scope.limit = campaignLength;
    };

    $scope.decrement = function() {
      $scope.limit = 4;
    };
  }
]);
<|endoftext|>


<commit_before>var request = require("request"),
    crypto = require("crypto"),
    querystring = require("querystring");

var BTCE = function(apiKey, secret, nonceGenerator) {
  var self = this;
  self.url = "https://btc-e.com/tapi";
  self.publicApiUrl = "https://btc-e.com/api/2/";
  self.apiKey = apiKey;
  self.secret = secret;
  self.nonce = nonceGenerator;

  self.makeRequest = function(method, params, callback) {
    var queryString,
        sign,
        headers;

    if(!self.apiKey || !self.secret) {
      callback(new Error("Must provide API key and secret to use the trade API."));
      return;
    }

    // If the user provided a function for generating the nonce, then use it.
    if(self.nonce) {
      params.nonce = self.nonce();
    } else {
      params.nonce = Math.round((new Date()).getTime() / 1000);
    }

    params.method = method;
    queryString = querystring.stringify(params);

    sign = crypto.createHmac("sha512", self.secret).update(new Buffer(queryString)).digest('hex').toString();
    headers = {
      'Sign': sign,
      'Key': self.apiKey
    };

    request({ url: self.url, method: "POST", form: params, headers: headers }, function(err, response, body) {
      if(err || response.statusCode !== 200) {
        return callback(new Error(err ? err : response.statusCode));
      }

      var result;
      try {
        result = JSON.parse(body);
      } catch(error) {
        return callback(new Error(error));
      }

      if(result.success === 0) {
        return callback(new Error(result.error));
      }

      callback(null, result['return']);
    });
  };

  self.makePublicApiRequest = function(pair, method, callback) {
    request({ url: self.publicApiUrl + pair + '/' + method }, function(err, response, body) {
      if(err || response.statusCode !== 200) {
        return callback(new Error(err ? err : response.statusCode));
      }

      var result;
      try {
        result = JSON.parse(body);
      } catch(error) {
        return callback(new Error(error));
      }

      if(result.error) {
        return callback(new Error(result.error));
      }

      callback(null, result);
    });
  };

  self.getInfo = function(callback) {
    self.makeRequest('getInfo', {}, callback);
  };

  self.transHistory = function(params, callback) {
    self.makeRequest('TransHistory', params, callback);
  };

  self.tradeHistory = function(params, callback) {
    self.makeRequest('TradeHistory', params, callback);
  };

  self.orderList = function(params, callback) {
    self.makeRequest('OrderList', params, callback);
  };

  self.trade = function(pair, type, rate, amount, callback) {
    self.makeRequest('Trade', {
      'pair': pair,
      'type': type,
      'rate': rate,
      'amount': amount
    }, callback);
  };

  self.cancelOrder = function(orderId, callback) {
    self.makeRequest('CancelOrder', {'order_id': orderId}, callback);
  };

  self.ticker = function(pair, callback) {
    self.makePublicApiRequest(pair, 'ticker', callback);
  };

  self.trades = function(pair, callback) {
    self.makePublicApiRequest(pair, 'trades', callback);
  };

  self.depth = function(pair, callback) {
    self.makePublicApiRequest(pair, 'depth', callback);
  };
};

module.exports = BTCE;
<commit_msg>remove unneeded error check for public api methods<commit_after>var request = require("request"),
    crypto = require("crypto"),
    querystring = require("querystring");

var BTCE = function(apiKey, secret, nonceGenerator) {
  var self = this;
  self.url = "https://btc-e.com/tapi";
  self.publicApiUrl = "https://btc-e.com/api/2/";
  self.apiKey = apiKey;
  self.secret = secret;
  self.nonce = nonceGenerator;

  self.makeRequest = function(method, params, callback) {
    var queryString,
        sign,
        headers;

    if(!self.apiKey || !self.secret) {
      callback(new Error("Must provide API key and secret to use the trade API."));
      return;
    }

    // If the user provided a function for generating the nonce, then use it.
    if(self.nonce) {
      params.nonce = self.nonce();
    } else {
      params.nonce = Math.round((new Date()).getTime() / 1000);
    }

    params.method = method;
    queryString = querystring.stringify(params);

    sign = crypto.createHmac("sha512", self.secret).update(new Buffer(queryString)).digest('hex').toString();
    headers = {
      'Sign': sign,
      'Key': self.apiKey
    };

    request({ url: self.url, method: "POST", form: params, headers: headers }, function(err, response, body) {
      if(err || response.statusCode !== 200) {
        return callback(new Error(err ? err : response.statusCode));
      }

      var result;
      try {
        result = JSON.parse(body);
      } catch(error) {
        return callback(new Error(error));
      }

      if(result.success === 0) {
        return callback(new Error(result.error));
      }

      callback(null, result['return']);
    });
  };

  self.makePublicApiRequest = function(pair, method, callback) {
    request({ url: self.publicApiUrl + pair + '/' + method }, function(err, response, body) {
      if(err || response.statusCode !== 200) {
        return callback(new Error(err ? err : response.statusCode));
      }

      var result;
      try {
        result = JSON.parse(body);
      } catch(error) {
        return callback(new Error(error));
      }

      callback(null, result);
    });
  };

  self.getInfo = function(callback) {
    self.makeRequest('getInfo', {}, callback);
  };

  self.transHistory = function(params, callback) {
    self.makeRequest('TransHistory', params, callback);
  };

  self.tradeHistory = function(params, callback) {
    self.makeRequest('TradeHistory', params, callback);
  };

  self.orderList = function(params, callback) {
    self.makeRequest('OrderList', params, callback);
  };

  self.trade = function(pair, type, rate, amount, callback) {
    self.makeRequest('Trade', {
      'pair': pair,
      'type': type,
      'rate': rate,
      'amount': amount
    }, callback);
  };

  self.cancelOrder = function(orderId, callback) {
    self.makeRequest('CancelOrder', {'order_id': orderId}, callback);
  };

  self.ticker = function(pair, callback) {
    self.makePublicApiRequest(pair, 'ticker', callback);
  };

  self.trades = function(pair, callback) {
    self.makePublicApiRequest(pair, 'trades', callback);
  };

  self.depth = function(pair, callback) {
    self.makePublicApiRequest(pair, 'depth', callback);
  };
};

module.exports = BTCE;
<|endoftext|>


<commit_before>define('app/models/machine', ['ember'],
    /**
     * Machine model
     *
     * Also check state mapping in config.py
     * @returns Class
     */
    function() {
        return Ember.Object.extend({
            id: null,

            imageId: null,

            image: null,
            name: null,
            backend: null,
            selected: false,
            hasKey: false,
            hasMonitoring: false,
            state: 'stopped',

            reboot: function(){
                log('Rebooting machine', this.name);

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=reboot',
                    success: function(data) {
                        that.set('state', 'rebooting');
                        info('Succesfully sent reboot to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending reboot to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending reboot to machine',
                                that.name);
                    }
                });
            },

            destroy: function(){
                log('Destroying machine', this.name);

                var that = this
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=destroy',
                    success: function(data) {
                        that.set('state', 'pending');
                        info('Successfully sent destroy to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending destroy to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending destroy to machine',
                                that.name);
                    }
                });
            },

            start: function(){
                log('Starting machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=start',
                    success: function(data) {
                        that.set('state', 'pending');
                        info('Successfully sent start to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending start to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending start to machine',
                                that.name);
                    }
                });
            },

            shutdown: function(){
                log('Stopping machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=stop',
                    success: function(data) {
                        that.set('state', 'stopped');
                        info('Successfully sent stop to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending stop to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending stop to machine',
                                that.name);
                    }
                });
            },

            shell: function(shell_command, callback){
                log('Sending', shell_command, 'to machine', this.name);

                var host;
                if (this.extra.dns_name) {
                    // it is an ec2 machine so it has dns_name
                    host = this.extra.dns_name;
                } else {
                    // if not in ec2, it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': shell_command},
                    success: function(data) {
                        if (data){
                            callback(data);
                        }
                        info('Successfully sent shell command', shell_command, 'to machine',
                                that.name, 'with result:\n', data);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error sending shell command ' +
                                shell_command + ' to machine ' + that.name);
                        error(textstate, errorThrown, 'when sending shell command',
                                shell_command, 'to machine', that.name);
                    }
                });

            },

            hasAlert : function(){
                //TODO when we have alerts
                return false;
            }.property('hasAlert'),

            startUptimeTimer: function() {
                var that = this;

                setInterval(function() {
                    if (that.get('state' != 'stopped') || !that.get('uptimeFromServer') ||
                        !that.get('uptimeChecked')) {

                        return;

                    } else {
                        that.set('uptime', that.get('uptimeFromServer') + (Date.now()
                                           - that.get('uptimeChecked')));
                    }
                }, 1000);
            },

            checkUptime: function(){
                if (this.hasKey) {
                    var host;
                    if (this.extra.dns_name) {
                        // it is ec2 machine
                        host = this.extra.dns_name;
                    } else {
                        // if not ec2 it should have a public ip
                        host = this.public_ips[0];
                    }

                    // In case of ec2, mist.io could have set this. Server can handle empty string.
                    var ssh_user;
                    try {
                        ssh_user = this.extra.tags.ssh_user;
                    } catch (error) {
                        ssh_user = 'root';
                    }

                    var that = this;
                    $.ajax({
                        url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                        type: 'POST',
                        data: {'host': host,
                               'ssh_user': ssh_user,
                               'command': 'cat /proc/uptime'},
                        success: function(data) {
                            var resp = data.split(' ');
                            if (resp.length == 2) {
                                var uptime = parseFloat(resp[0]) * 1000;
                                that.set('uptimeChecked', Date.now());
                                that.set('uptimeFromServer', uptime);
                            }
                            info('Successfully got uptime', data, 'from machine', that.name);
                        },
                        error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error getting uptime from machine ' +
                                    that.name);
                            error(textstate, errorThrown, 'when getting uptime from machine',
                                    that.name);
                        }
                    });
                }
            },

            checkHasMonitoring: function(){
                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    success: function(data) {
                        log("machine has monitoring");
                        log(data);
                        if('monitoring' in data){
                            that.set('hasMonitoring', data.monitoring);
                        }
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error checking monitoring of machine ' +
                                    that.name);
                            error(textstate, errorThrown, 'while checking monitoring of machine',
                                    that.name);
                    }
                });
            },

            checkHasKey: function(){
                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': 'cat /proc/uptime'},
                    success: function(data) {
                        that.set('hasKey', true);
                        // TODO: since we got it let's use it, checkUptime does the same.
                        var resp = data.split(' ');
                        if (resp.length == 2) {
                            var uptime = parseFloat(resp[0]) * 1000;
                            that.set('uptimeChecked', Date.now());
                            that.set('uptimeFromServer', uptime);
                        }
                        info('We have key for machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasKey', false);
                        Mist.notificationController.notify('Error while checking key of machine ' +
                                that.name);
                        error(textstate, errorThrown, 'while checking key of machine', that.name);
                    }
                });
            },

            resetUptime: function(){
                if (this.state != 'stopped') {
                    this.set('uptime', 0);
                    this.uptimeTimer = false;
                } else {
                    if (this.get('uptime') == 0) {
                        // TODO: This is used only here, can we skip checkUptime?
                        this.checkUptime();
                    }
                }
            }.observes('state'),

            monitoringChanged: function(){
                var oldValue = !this.hasMonitoring;
                log("monitoring:  " + oldValue);

                var that = this;

                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                var payload = {
                   'monitoring': this.hasMonitoring,
                   'host': host,
                   'provider': this.backend.provider
                };

                $.ajax({
                    // TODO: this should point to https://mist.io/....
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify(payload),
                    dataType: 'json',
                    success: function(data) {

                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasMonitoring', oldValue);
                    }
                });
            }.observes('hasMonitoring'),

            init: function() {
                this._super();
                var that = this;
                this.backend.images.getImage(this.imageId, function(image) {
                    that.set('image', image);
                });
                this.startUptimeTimer();
                this.checkHasKey();
                this.checkHasMonitoring();
            }

        });
    }
);
<commit_msg>Deprecate checkHasKey, everything is done by checkUptime now.<commit_after>define('app/models/machine', ['ember'],
    /**
     * Machine model
     *
     * Also check state mapping in config.py
     * @returns Class
     */
    function() {
        return Ember.Object.extend({
            id: null,

            imageId: null,

            image: null,
            name: null,
            backend: null,
            selected: false,
            hasKey: false,
            hasMonitoring: false,
            state: 'stopped',

            reboot: function(){
                log('Rebooting machine', this.name);

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=reboot',
                    success: function(data) {
                        that.set('state', 'rebooting');
                        info('Succesfully sent reboot to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending reboot to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending reboot to machine',
                                that.name);
                    }
                });
            },

            destroy: function(){
                log('Destroying machine', this.name);

                var that = this
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=destroy',
                    success: function(data) {
                        that.set('state', 'pending');
                        info('Successfully sent destroy to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending destroy to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending destroy to machine',
                                that.name);
                    }
                });
            },

            start: function(){
                log('Starting machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=start',
                    success: function(data) {
                        that.set('state', 'pending');
                        info('Successfully sent start to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending start to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending start to machine',
                                that.name);
                    }
                });
            },

            shutdown: function(){
                log('Stopping machine', this.name);

                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id,
                    type: 'POST',
                    data: 'action=stop',
                    success: function(data) {
                        that.set('state', 'stopped');
                        info('Successfully sent stop to machine', that.name);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error when sending stop to machine ' +
                                that.name);
                        error(textstate, errorThrown, 'when sending stop to machine',
                                that.name);
                    }
                });
            },

            shell: function(shell_command, callback){
                log('Sending', shell_command, 'to machine', this.name);

                var host;
                if (this.extra.dns_name) {
                    // it is an ec2 machine so it has dns_name
                    host = this.extra.dns_name;
                } else {
                    // if not in ec2, it should have a public ip
                    host = this.public_ips[0];
                }

                // In case of ec2, mist.io could have set this. Server can handle empty string.
                var ssh_user;
                try {
                    ssh_user = this.extra.tags.ssh_user;
                } catch (error) {
                    ssh_user = 'root';
                }

                var that = this;
                $.ajax({
                    url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                    type: 'POST',
                    data: {'host': host,
                           'ssh_user': ssh_user,
                           'command': shell_command},
                    success: function(data) {
                        if (data){
                            callback(data);
                        }
                        info('Successfully sent shell command', shell_command, 'to machine',
                                that.name, 'with result:\n', data);
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        Mist.notificationController.notify('Error sending shell command ' +
                                shell_command + ' to machine ' + that.name);
                        error(textstate, errorThrown, 'when sending shell command',
                                shell_command, 'to machine', that.name);
                    }
                });

            },

            hasAlert : function(){
                //TODO when we have alerts
                return false;
            }.property('hasAlert'),

            startUptimeTimer: function() {
                var that = this;

                setInterval(function() {
                    if (that.get('state' != 'stopped') || !that.get('uptimeFromServer') ||
                        !that.get('uptimeChecked')) {

                        return;

                    } else {
                        that.set('uptime', that.get('uptimeFromServer') + (Date.now()
                                           - that.get('uptimeChecked')));
                    }
                }, 1000);
            },

            checkUptime: function(){
                if (this.hasKey) {
                    var host;
                    if (this.extra.dns_name) {
                        // it is ec2 machine
                        host = this.extra.dns_name;
                    } else {
                        // if not ec2 it should have a public ip
                        host = this.public_ips[0];
                    }

                    // In case of ec2, mist.io could have set this. Server can handle empty string.
                    var ssh_user;
                    try {
                        ssh_user = this.extra.tags.ssh_user;
                    } catch (error) {
                        ssh_user = 'root';
                    }

                    var that = this;
                    $.ajax({
                        url: '/backends/' + this.backend.index + '/machines/' + this.id + '/shell',
                        type: 'POST',
                        data: {'host': host,
                               'ssh_user': ssh_user,
                               'command': 'cat /proc/uptime'},
                        success: function(data) {
                            // also means in has a key
                            that.set('hasKey', true);
                            var resp = data.split(' ');
                            if (resp.length == 2) {
                                var uptime = parseFloat(resp[0]) * 1000;
                                that.set('uptimeChecked', Date.now());
                                that.set('uptimeFromServer', uptime);
                            }
                            info('Successfully got uptime', data, 'from machine', that.name);
                        },
                        error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error getting uptime from machine ' +
                                    that.name);
                            error(textstate, errorThrown, 'when getting uptime from machine',
                                    that.name);
                        }
                    });
                }
            },

            checkHasMonitoring: function(){
                var that = this;
                $.ajax({
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    success: function(data) {
                        log("machine has monitoring");
                        log(data);
                        if('monitoring' in data){
                            that.set('hasMonitoring', data.monitoring);
                        }
                    },
                    error: function(jqXHR, textstate, errorThrown) {
                            Mist.notificationController.notify('Error checking monitoring of machine ' +
                                    that.name);
                            error(textstate, errorThrown, 'while checking monitoring of machine',
                                    that.name);
                    }
                });
            },

            resetUptime: function() {
                if (this.state != 'stopped') {
                    this.set('uptime', 0);
                    this.uptimeTimer = false;
                } else {
                    if (this.get('uptime') == 0) {
                        this.checkUptime();
                    }
                }
            }.observes('state'),

            monitoringChanged: function(){
                var oldValue = !this.hasMonitoring;
                log("monitoring:  " + oldValue);

                var that = this;

                var host;
                if (this.extra.dns_name) {
                    // it is ec2 machine
                    host = this.extra.dns_name;
                } else {
                    // if not ec2 it should have a public ip
                    host = this.public_ips[0];
                }

                var payload = {
                   'monitoring': this.hasMonitoring,
                   'host': host,
                   'provider': this.backend.provider
                };

                $.ajax({
                    // TODO: this should point to https://mist.io/....
                    url: 'backends/' + this.backend.index + '/machines/' + this.id + '/monitoring',
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify(payload),
                    dataType: 'json',
                    success: function(data) {

                    },
                    error: function(jqXHR, textstate, errorThrown) {
                        that.set('hasMonitoring', oldValue);
                    }
                });
            }.observes('hasMonitoring'),

            init: function() {
                this._super();
                var that = this;
                this.backend.images.getImage(this.imageId, function(image) {
                    that.set('image', image);
                });
                this.startUptimeTimer();
                this.checkUptime();
                this.checkHasMonitoring();
            }

        });
    }
);
<|endoftext|>


<commit_before>/* @flow weak */

/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React, {
  Component,
  StyleSheet,
  View,
} from 'react-native';

import { Navigator } from './navigation';

import {
  CustomerInvoicePage,
  CustomerInvoicesPage,
  CustomerPage,
  CustomersPage,
  FirstUsePage,
  MenuPage,
  StockHistoriesPage,
  StockHistoryPage,
  StockPage,
  StocktakeEditPage,
  StocktakeManagePage,
  StocktakesPage,
  SupplierInvoicePage,
  SupplierInvoicesPage,
} from './pages';

import { Button, LoginModal } from './widgets';

import { Synchronizer } from './sync';
import { SyncAuthenticator, UserAuthenticator } from './authentication';
import realm from './database/realm';
import Scheduler from './Scheduler';
import globalStyles from './globalStyles';

// TODO: oh god delete this
console.log('DELETE THIS LINE AND THE ONE BELOW');
import mockit from './database/mockDBInstantiator';

const SYNC_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds
const AUTHENTICATION_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds

export default class OfflineMobileApp extends Component {

  constructor() {
    super();
    this.userAuthenticator = new UserAuthenticator(realm);
    this.synchronizer = new Synchronizer(realm, new SyncAuthenticator(realm));
    this.scheduler = new Scheduler();
    const initialised = this.synchronizer.isInitialised();
    console.log('DELETE THIS LINE AND THE ONE BELOW');
    mockit();
    this.state = {
      initialised: true,
      authenticated: true,
    };
  }

  componentWillMount() {
    this.renderScene = this.renderScene.bind(this);
    this.onAuthentication = this.onAuthentication.bind(this);
    this.onInitialised = this.onInitialised.bind(this);
    this.scheduler.schedule(this.synchronizer.synchronize,
                            SYNC_INTERVAL);
    this.scheduler.schedule(() => this.userAuthenticator.reauthenticate(this.onAuthentication),
                            AUTHENTICATION_INTERVAL);
  }

  componentWillUnmount() {
    this.scheduler.clearAll();
  }

  onAuthentication(authenticated) {
    this.setState({ authenticated: authenticated });
  }

  onInitialised() {
    this.setState({ initialised: true });
  }

  renderScene(props) {
    const navigateTo = (key, title) => {
      props.onNavigate({ type: 'push', key, title });
    };
    switch (props.scene.navigationState.key) {
      case 'menu':
        return <MenuPage navigateTo={navigateTo} />;
      case 'customers':
        return <CustomersPage navigateTo={navigateTo} />;
      case 'customer':
        return <CustomerPage navigateTo={navigateTo} />;
      case 'stock':
        return <StockPage database={realm} navigateTo={navigateTo} />;
      case 'stocktakes':
        return <StocktakesPage database={realm} navigateTo={navigateTo} />;
      case 'stocktakeEditor':
        return <StocktakeEditPage navigateTo={navigateTo} />;
      case 'stocktakeManager':
        return <StocktakeManagePage navigateTo={navigateTo} />;
      case 'customerInvoices':
        return <CustomerInvoicesPage database={realm} navigateTo={navigateTo} />;
      case 'customerInvoice':
        return <CustomerInvoicePage navigateTo={navigateTo} />;
      case 'supplierInvoices':
        return <SupplierInvoicesPage database={realm} navigateTo={navigateTo} />;
      case 'supplierInvoice':
        return <SupplierInvoicePage navigateTo={navigateTo} />;
      case 'stockHistories':
        return <StockHistoriesPage navigateTo={navigateTo} />;
      case 'stockHistory':
        return <StockHistoryPage navigateTo={navigateTo} />;
      case 'root':
      default:
        return <MenuPage navigateTo={navigateTo} />;
    }
  }

  render() {
    if (!this.state.initialised) {
      return (
        <FirstUsePage
          synchronizer={this.synchronizer}
          onInitialised={this.onInitialised}
        />
      );
    }
    return (
      <View style={styles.container}>
        <Navigator
          renderScene={this.renderScene}
        />
        <LoginModal
          authenticator={this.userAuthenticator}
          isAuthenticated={this.state.authenticated}
          onAuthentication={this.onAuthentication}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
<commit_msg>Remove login skip hack. Removed unused imports<commit_after>/* @flow weak */

/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React, {
  Component,
  StyleSheet,
  View,
} from 'react-native';

import { Navigator } from './navigation';

import {
  CustomerInvoicePage,
  CustomerInvoicesPage,
  CustomerPage,
  CustomersPage,
  FirstUsePage,
  MenuPage,
  StockHistoriesPage,
  StockHistoryPage,
  StockPage,
  StocktakeEditPage,
  StocktakeManagePage,
  StocktakesPage,
  SupplierInvoicePage,
  SupplierInvoicesPage,
} from './pages';

import { LoginModal } from './widgets';

import { Synchronizer } from './sync';
import { SyncAuthenticator, UserAuthenticator } from './authentication';
import realm from './database/realm';
import Scheduler from './Scheduler';

const SYNC_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds
const AUTHENTICATION_INTERVAL = 10 * 60 * 1000; // 10 minutes in milliseconds

export default class OfflineMobileApp extends Component {

  constructor() {
    super();
    this.userAuthenticator = new UserAuthenticator(realm);
    this.synchronizer = new Synchronizer(realm, new SyncAuthenticator(realm));
    this.scheduler = new Scheduler();
    const initialised = this.synchronizer.isInitialised();
    this.state = {
      initialised: initialised,
      authenticated: false,
    };
  }

  componentWillMount() {
    this.renderScene = this.renderScene.bind(this);
    this.onAuthentication = this.onAuthentication.bind(this);
    this.onInitialised = this.onInitialised.bind(this);
    this.scheduler.schedule(this.synchronizer.synchronize,
                            SYNC_INTERVAL);
    this.scheduler.schedule(() => this.userAuthenticator.reauthenticate(this.onAuthentication),
                            AUTHENTICATION_INTERVAL);
  }

  componentWillUnmount() {
    this.scheduler.clearAll();
  }

  onAuthentication(authenticated) {
    this.setState({ authenticated: authenticated });
  }

  onInitialised() {
    this.setState({ initialised: true });
  }

  renderScene(props) {
    const navigateTo = (key, title) => {
      props.onNavigate({ type: 'push', key, title });
    };
    switch (props.scene.navigationState.key) {
      case 'menu':
        return <MenuPage navigateTo={navigateTo} />;
      case 'customers':
        return <CustomersPage navigateTo={navigateTo} />;
      case 'customer':
        return <CustomerPage navigateTo={navigateTo} />;
      case 'stock':
        return <StockPage database={realm} navigateTo={navigateTo} />;
      case 'stocktakes':
        return <StocktakesPage database={realm} navigateTo={navigateTo} />;
      case 'stocktakeEditor':
        return <StocktakeEditPage navigateTo={navigateTo} />;
      case 'stocktakeManager':
        return <StocktakeManagePage navigateTo={navigateTo} />;
      case 'customerInvoices':
        return <CustomerInvoicesPage database={realm} navigateTo={navigateTo} />;
      case 'customerInvoice':
        return <CustomerInvoicePage navigateTo={navigateTo} />;
      case 'supplierInvoices':
        return <SupplierInvoicesPage database={realm} navigateTo={navigateTo} />;
      case 'supplierInvoice':
        return <SupplierInvoicePage navigateTo={navigateTo} />;
      case 'stockHistories':
        return <StockHistoriesPage navigateTo={navigateTo} />;
      case 'stockHistory':
        return <StockHistoryPage navigateTo={navigateTo} />;
      case 'root':
      default:
        return <MenuPage navigateTo={navigateTo} />;
    }
  }

  render() {
    if (!this.state.initialised) {
      return (
        <FirstUsePage
          synchronizer={this.synchronizer}
          onInitialised={this.onInitialised}
        />
      );
    }
    return (
      <View style={styles.container}>
        <Navigator
          renderScene={this.renderScene}
        />
        <LoginModal
          authenticator={this.userAuthenticator}
          isAuthenticated={this.state.authenticated}
          onAuthentication={this.onAuthentication}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});
<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class PitTrap extends DrawCard {
  setupCardAbilities(ability) {
      this.whileAttached({
          condition: () => (
              this.game.currentPhase === 'regroup'
          ),
          effect: ability.effects.cannotBeReadied()
      });
  }

  canAttach(card) {
      if(this.game.currentConflict && this.game.currentConflict.isAttacking(card)) {
          return super.canAttach(card);
      }
      return false;
  }
}

PitTrap.id = 'pit-trap';

module.exports = PitTrap;
<commit_msg>Fixed eslint issues<commit_after>const DrawCard = require('../../drawcard.js');

class PitTrap extends DrawCard {
    setupCardAbilities(ability) {
        this.whileAttached({
            condition: () => (
                this.game.currentPhase === 'regroup'
            ),
            effect: ability.effects.cannotBeReadied()
        });
    }

    canAttach(card) {
        if(this.game.currentConflict && this.game.currentConflict.isAttacking(card)) {
            return super.canAttach(card);
        }
        return false;
    }
}

PitTrap.id = 'pit-trap';

module.exports = PitTrap;
<|endoftext|>


<commit_before>import gql from "graphql-tag"

// used in Availability component (homepage)
const GET_STOCK_TOTALS = gql`
  query StockList($cursor: Int!) {
    listPlasmids(input: { cursor: $cursor, limit: 30000 }) {
      totalCount
    }
    listStrains(input: { cursor: $cursor, limit: 30000 }) {
      totalCount
    }
  }
`

const GET_STRAIN_LIST = gql`
  query StrainList($cursor: Int!, $filter: String!) {
    listStrains(input: { cursor: $cursor, limit: 10, filter: $filter }) {
      nextCursor
      strains {
        id
        label
        summary
        in_stock
      }
    }
  }
`

const GET_PLASMID_LIST = gql`
  query PlasmidListFilter($cursor: Int!, $filter: String!) {
    listPlasmids(input: { cursor: $cursor, limit: 10, filter: $filter }) {
      nextCursor
      plasmids {
        id
        name
        summary
        in_stock
      }
    }
  }
`

const GET_RELATED_STRAINS = gql`
  query StrainList($filter: String!) {
    listStrains(input: { limit: 10, filter: $filter }) {
      strains {
        id
        label
      }
    }
  }
`

const GET_PLASMID = gql`
  query Plasmid($id: ID!) {
    plasmid(id: $id) {
      id
      name
      summary
      depositor
      publications {
        id
        doi
      }
      dbxrefs
      genes
      image_map
      sequence
      keywords
      genbank_accession
      in_stock
    }
  }
`

const GET_STRAIN = gql`
  query Strain($id: ID!) {
    strain(id: $id) {
      id
      label
      summary
      species
      parent {
        id
        label
      }
      depositor
      plasmid
      dbxrefs
      publications {
        id
        doi
      }
      genes
      in_stock
      systematic_name
      genotypes
      mutagenesis_method
      genetic_modification
      names
      characteristics
    }
  }
`

/**
 * query will still need these from annotations:
      phenotypes {
        phenotype
        note
        assay
        environment
        publication {
         doi
         id
        }
      }
 */

const GET_CONTENT_BY_SLUG = gql`
  query contentBySlug($slug: String!) {
    contentBySlug(slug: $slug) {
      id
      content
      name
      slug
      created_by {
        email
        roles {
          role
          permissions {
            permission
            resource
          }
        }
      }
      updated_by {
        email
        roles {
          role
          permissions {
            permission
            resource
          }
        }
      }
    }
  }
`

const GET_USER = gql`
  query GetUserByID($id: ID!) {
    user(id: $id) {
      first_name
      last_name
      roles {
        role
        permissions {
          permission
          resource
        }
      }
    }
  }
`

const GET_REFRESH_TOKEN = gql`
  query GetRefreshToken($token: String!) {
    getRefreshToken(token: $token) {
      token
    }
  }
`

// MUTATIONS

const LOGIN = gql`
  mutation Login($input: LoginInput!) {
    login(input: $input) {
      token
      user {
        id
        email
        first_name
        last_name
        roles {
          role
          permissions {
            permission
          }
        }
      }
      identity {
        provider
      }
    }
  }
`

const LOGOUT = gql`
  mutation Logout {
    logout {
      success
    }
  }
`

const POST_ORDER = gql`
  mutation CreateOrder($input: CreateOrderInput!) {
    createOrder(input: $input) {
      id
    }
  }
`

const CREATE_CONTENT = gql`
  mutation CreateContent($input: CreateContentInput!) {
    createContent(input: $input) {
      name
      created_by {
        id
      }
      content
      namespace
    }
  }
`

const UPDATE_CONTENT = gql`
  mutation UpdateContent($input: UpdateContentInput!) {
    updateContent(input: $input) {
      id
      updated_by {
        id
      }
      content
    }
  }
`

export {
  GET_STOCK_TOTALS,
  GET_STRAIN_LIST,
  GET_PLASMID_LIST,
  GET_RELATED_STRAINS,
  GET_STRAIN,
  GET_PLASMID,
  GET_CONTENT_BY_SLUG,
  GET_USER,
  GET_REFRESH_TOKEN,
  LOGIN,
  LOGOUT,
  POST_ORDER,
  CREATE_CONTENT,
  UPDATE_CONTENT,
}
<commit_msg>refactor: get user info from content query instead<commit_after>import gql from "graphql-tag"

// used in Availability component (homepage)
const GET_STOCK_TOTALS = gql`
  query StockList($cursor: Int!) {
    listPlasmids(input: { cursor: $cursor, limit: 30000 }) {
      totalCount
    }
    listStrains(input: { cursor: $cursor, limit: 30000 }) {
      totalCount
    }
  }
`

const GET_STRAIN_LIST = gql`
  query StrainList($cursor: Int!, $filter: String!) {
    listStrains(input: { cursor: $cursor, limit: 10, filter: $filter }) {
      nextCursor
      strains {
        id
        label
        summary
        in_stock
      }
    }
  }
`

const GET_PLASMID_LIST = gql`
  query PlasmidListFilter($cursor: Int!, $filter: String!) {
    listPlasmids(input: { cursor: $cursor, limit: 10, filter: $filter }) {
      nextCursor
      plasmids {
        id
        name
        summary
        in_stock
      }
    }
  }
`

const GET_RELATED_STRAINS = gql`
  query StrainList($filter: String!) {
    listStrains(input: { limit: 10, filter: $filter }) {
      strains {
        id
        label
      }
    }
  }
`

const GET_PLASMID = gql`
  query Plasmid($id: ID!) {
    plasmid(id: $id) {
      id
      name
      summary
      depositor
      publications {
        id
        doi
      }
      dbxrefs
      genes
      image_map
      sequence
      keywords
      genbank_accession
      in_stock
    }
  }
`

const GET_STRAIN = gql`
  query Strain($id: ID!) {
    strain(id: $id) {
      id
      label
      summary
      species
      parent {
        id
        label
      }
      depositor
      plasmid
      dbxrefs
      publications {
        id
        doi
      }
      genes
      in_stock
      systematic_name
      genotypes
      mutagenesis_method
      genetic_modification
      names
      characteristics
    }
  }
`

/**
 * query will still need these from annotations:
      phenotypes {
        phenotype
        note
        assay
        environment
        publication {
         doi
         id
        }
      }
 */

const GET_CONTENT_BY_SLUG = gql`
  query contentBySlug($slug: String!) {
    contentBySlug(slug: $slug) {
      id
      content
      name
      slug
      created_by {
        id
        email
        first_name
        last_name
        roles {
          role
          permissions {
            permission
            resource
          }
        }
      }
      updated_by {
        id
        email
        first_name
        last_name
        updated_at
        roles {
          role
          permissions {
            permission
            resource
          }
        }
      }
    }
  }
`

const GET_REFRESH_TOKEN = gql`
  query GetRefreshToken($token: String!) {
    getRefreshToken(token: $token) {
      token
    }
  }
`

// MUTATIONS

const LOGIN = gql`
  mutation Login($input: LoginInput!) {
    login(input: $input) {
      token
      user {
        id
        email
        first_name
        last_name
        roles {
          role
          permissions {
            permission
          }
        }
      }
      identity {
        provider
      }
    }
  }
`

const LOGOUT = gql`
  mutation Logout {
    logout {
      success
    }
  }
`

const POST_ORDER = gql`
  mutation CreateOrder($input: CreateOrderInput!) {
    createOrder(input: $input) {
      id
    }
  }
`

const CREATE_CONTENT = gql`
  mutation CreateContent($input: CreateContentInput!) {
    createContent(input: $input) {
      name
      created_by {
        id
      }
      content
      namespace
    }
  }
`

const UPDATE_CONTENT = gql`
  mutation UpdateContent($input: UpdateContentInput!) {
    updateContent(input: $input) {
      id
      updated_by {
        id
      }
      content
    }
  }
`

export {
  GET_STOCK_TOTALS,
  GET_STRAIN_LIST,
  GET_PLASMID_LIST,
  GET_RELATED_STRAINS,
  GET_STRAIN,
  GET_PLASMID,
  GET_CONTENT_BY_SLUG,
  GET_REFRESH_TOKEN,
  LOGIN,
  LOGOUT,
  POST_ORDER,
  CREATE_CONTENT,
  UPDATE_CONTENT,
}
<|endoftext|>


<commit_before>import R from 'ramda';
import shortid from 'shortid';
import * as XP from 'xod-project';

import {
  EDITOR_DESELECT_ALL,
  EDITOR_SELECT_ENTITY,
  EDITOR_DESELECT_ENTITY,
  EDITOR_ADD_ENTITY_TO_SELECTION,
  EDITOR_SELECT_PIN,
  EDITOR_DESELECT_PIN,
  EDITOR_SET_SELECTED_NODETYPE,
  EDITOR_SWITCH_PATCH,
  EDITOR_SWITCH_TAB,
  TAB_CLOSE,
  TAB_SORT,
  SET_CURRENT_PATCH_OFFSET,
  TOGGLE_HELPBAR,
  SET_FOCUSED_AREA,
  SHOW_SUGGESTER,
  HIDE_SUGGESTER,
  HIGHLIGHT_SUGGESTER_ITEM,
  START_DRAGGING_PATCH,
  PASTE_ENTITIES,
} from './actionTypes';
import {
  PROJECT_CREATE,
  PROJECT_OPEN,
  PROJECT_IMPORT,
  PROJECT_OPEN_WORKSPACE,
  PATCH_ADD,
  PATCH_DELETE,
  PATCH_RENAME,
  NODE_ADD,
  LINK_ADD,
  BULK_DELETE_ENTITIES,
} from '../project/actionTypes';
import {
  DEBUG_SESSION_STARTED,
  DEBUG_SESSION_STOPPED,
} from '../debugger/actionTypes';

import { DEFAULT_PANNING_OFFSET } from '../project/nodeLayout';
import { TAB_TYPES, EDITOR_MODE, SELECTION_ENTITY_TYPE } from './constants';
import { getTabByPatchPath } from './selectors';
import { switchPatchUnsafe } from './actions';

// =============================================================================
//
// Utils
//
// =============================================================================

const getTabs = R.prop('tabs');

const getTabById = R.curry(
  (tabId, state) => R.compose(
    R.propOr(null, tabId),
    getTabs
  )(state)
);

const isTabOpened = R.curry(
  (tabId, state) => R.compose(
    R.complement(R.isNil),
    getTabById(tabId)
  )(state)
);

const isPatchOpened = R.curry(
  (patchPath, state) => R.compose(
    R.complement(R.isNil),
    getTabByPatchPath(patchPath),
    getTabs
  )(state)
);

const setTabOffset = R.curry(
  (offset, tabId, state) => R.assocPath(
    ['tabs', tabId, 'offset'],
    offset,
    state
  )
);

const getTabIdbyPatchPath = R.curry(
  (patchPath, state) => R.compose(
    R.propOr(null, 'id'),
    R.find(R.propEq('patchPath', patchPath)),
    R.values,
    getTabs
  )(state)
);

const listTabsByPatchPath = R.curry(
  (patchPath, state) => R.compose(
    R.filter(R.propEq('patchPath', patchPath)),
    R.values,
    getTabs
  )(state)
);

const syncTabOffset = R.curry(
  (offset, state) => {
    const currentTab = getTabById(state.currentTabId, state);
    const currentPatchPath = currentTab.patchPath;
    if (!currentTab) return state;

    const tabIdsToSync = R.compose(
      R.map(R.prop('id')),
      R.reject(R.propEq('id', state.currentTabId)),
      listTabsByPatchPath(currentPatchPath)
    )(state);
    const syncOffsets = R.map(setTabOffset(offset), tabIdsToSync);

    return R.reduce((acc, fn) => fn(acc), state, syncOffsets);
  }
);

const addTabWithProps = R.curry(
  (id, type, patchPath, state) => {
    const tabs = R.prop('tabs')(state);
    const lastIndex = R.reduce(
      (acc, tab) => R.pipe(
        R.prop('index'),
        R.max(acc)
      )(tab),
      -1,
      R.values(tabs)
    );
    const newIndex = R.inc(lastIndex);

    return R.assocPath(['tabs', id], {
      id,
      patchPath,
      index: newIndex,
      type,
      offset: DEFAULT_PANNING_OFFSET,
    }, state);
  }
);

const addPatchTab = R.curry((newId, patchPath, state) => {
  if (!patchPath) return state;
  return addTabWithProps(newId, TAB_TYPES.PATCH, patchPath, state);
});

const applyTabSort = (tab, payload) => {
  if (R.not(R.has(tab.id, payload))) { return tab; }

  return R.assoc('index', payload[tab.id].index, tab);
};

const resetCurrentPatchPath = (reducer, state, payload) => {
  const newState = R.assoc('tabs', {}, state);
  const firstPatchPath = R.pipe(
    XP.listLocalPatches,
    R.head,
    R.ifElse(
      R.isNil,
      R.always(null),
      XP.getPatchPath
    )
  )(payload);

  return reducer(newState, switchPatchUnsafe(firstPatchPath));
};

const clearSelection = R.flip(R.merge)({
  selection: [],
  linkingPin: null,
});

const openPatchByPath = R.curry(
  (patchPath, state) => {
    const alreadyOpened = isPatchOpened(patchPath, state);
    const tabId = (alreadyOpened) ?
        getTabIdbyPatchPath(patchPath, state) :
        shortid.generate();

    return R.compose(
      R.assoc('currentTabId', tabId),
      clearSelection,
      R.unless(
        () => alreadyOpened,
        addPatchTab(tabId, patchPath)
      )
    )(state);
  }
);

const openLatestOpenedTab = R.converge(
  R.assoc('currentTabId'),
  [
    R.compose( // get patch id from last of remaining tabs
      R.propOr(null, 'id'),
      R.last,
      R.values,
      R.prop('tabs')
    ),
    R.identity,
  ]
);

const closeTabById = R.curry(
  (tabId, state) => {
    const tabToClose = getTabById(tabId, state);

    const isDebuggerTabClosing = () => (tabToClose.type === TAB_TYPES.DEBUGGER);
    const isCurrentTabClosing = R.propEq('currentTabId', tabId);
    const isCurrentDebuggerTabClosing = R.both(
      isDebuggerTabClosing,
      isCurrentTabClosing
    );

    const openOriginalPatch = patchPath => R.compose(
      R.converge(
        setTabOffset(tabToClose.offset),
        [
          getTabIdbyPatchPath(patchPath),
          R.identity,
        ]
      ),
      openPatchByPath(patchPath)
    );

    return R.compose(
      R.cond([
        [isCurrentDebuggerTabClosing, openOriginalPatch(tabToClose.patchPath)],
        [isCurrentTabClosing, openLatestOpenedTab],
        [R.T, R.identity],
      ]),
      R.dissocPath(['tabs', tabId])
    )(state);
  }
);

const closeTabByPatchPath = (patchPath, state) => {
  const tabIdToClose = getTabIdbyPatchPath(patchPath, state);
  return closeTabById(tabIdToClose, state);
};

const renamePatchInTabs = (newPatchPath, oldPatchPath) => (tabs) => {
  const oldPatchTab = R.prop(oldPatchPath, tabs);

  if (!oldPatchTab) return tabs;

  return R.compose(
    R.dissoc(oldPatchPath),
    R.assoc(
      newPatchPath,
      R.assoc(
        'patchPath',
        newPatchPath,
        oldPatchTab
      )
    )
  )(tabs);
};

const createSelectionEntity = R.curry((entityType, id) => ({ entity: entityType, id }));

// =============================================================================
//
// Reducer
//
// =============================================================================

const editorReducer = (state = {}, action) => {
  switch (action.type) {
    case BULK_DELETE_ENTITIES:
    case EDITOR_DESELECT_ALL:
      return clearSelection(state);
    case EDITOR_SELECT_ENTITY:
      return R.assoc(
        'selection',
        [
          createSelectionEntity(
            action.payload.entityType,
            action.payload.id
          ),
        ],
        state
      );
    case EDITOR_DESELECT_ENTITY:
      return R.over(
        R.lensProp('selection'),
        R.reject(R.equals(createSelectionEntity(
          action.payload.entityType,
          action.payload.id
        ))),
        state
      );
    case EDITOR_ADD_ENTITY_TO_SELECTION:
      return R.over(
        R.lensProp('selection'),
        R.compose(
          R.uniq,
          R.append(createSelectionEntity(
            action.payload.entityType,
            action.payload.id
          ))
        ),
        state
      );
    case PASTE_ENTITIES: {
      const { entities } = action.payload;

      const newSelection = R.compose(
        R.unnest,
        R.values,
        R.evolve({
          nodes: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.NODE),
            XP.getNodeId
          )),
          comments: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.COMMENT),
            XP.getCommentId
          )),
          links: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.LINK),
            XP.getLinkId
          )),
        })
      )(entities);

      return R.assoc(
        'selection',
        newSelection,
        state
      );
    }
    case EDITOR_SELECT_PIN:
      return R.assoc('linkingPin', action.payload, state);
    case EDITOR_DESELECT_PIN:
    case LINK_ADD:
      return R.assoc('linkingPin', null, state);
    case START_DRAGGING_PATCH:
      return R.merge(
        state,
        {
          mode: EDITOR_MODE.ACCEPTING_DRAGGED_PATCH,
          draggedPreviewSize: action.payload,
        }
      );
    case NODE_ADD:
      return R.assoc('draggedPreviewSize', { width: 0, height: 0 }, state);
    case EDITOR_SET_SELECTED_NODETYPE:
      return R.assoc('selectedNodeType', action.payload.id, state);
    case PROJECT_CREATE: {
      const newState = R.assoc('tabs', {}, state);
      return editorReducer(newState, switchPatchUnsafe(action.payload.mainPatchPath));
    }
    case PROJECT_OPEN:
    case PROJECT_IMPORT: {
      const newState = R.merge(state, {
        currentTabId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
      return resetCurrentPatchPath(editorReducer, newState, action.payload);
    }
    case PROJECT_OPEN_WORKSPACE:
      return R.merge(state, {
        currentTabId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
    case PATCH_ADD:
    case EDITOR_SWITCH_PATCH:
      return openPatchByPath(action.payload.patchPath, state);
    case EDITOR_SWITCH_TAB:
      return R.assoc(
        'currentTabId',
        action.payload.tabId,
        state
      );
    case PATCH_RENAME: {
      const { newPatchPath, oldPatchPath } = action.payload;

      return R.over(
        R.lensProp('tabs'),
        renamePatchInTabs(newPatchPath, oldPatchPath),
        state
      );
    }
    case PATCH_DELETE:
      return closeTabByPatchPath(action.payload.patchPath, state);
    case TAB_CLOSE:
      return closeTabById(action.payload.id, state);
    case TAB_SORT:
      return R.assoc(
        'tabs',
        R.reduce(
          (p, cur) => R.assoc(cur.id, applyTabSort(cur, action.payload), p),
          {},
          R.values(state.tabs)
        ),
        state
      );
    case SET_CURRENT_PATCH_OFFSET: {
      return R.compose(
        syncTabOffset(action.payload),
        setTabOffset(action.payload, state.currentTabId)
      )(state);
    }
    case TOGGLE_HELPBAR:
      return R.over(R.lensProp('isHelpbarVisible'), R.not, state);
    case SET_FOCUSED_AREA:
      return R.assoc('focusedArea', action.payload, state);
    case SHOW_SUGGESTER: {
      if (R.path(['suggester', 'visible'], state) === true) return state;

      return R.compose(
        R.assocPath(['suggester', 'visible'], true),
        R.assocPath(['suggester', 'placePosition'], action.payload)
      )(state);
    }
    case HIDE_SUGGESTER:
      return R.compose(
        R.assocPath(['suggester', 'visible'], false),
        R.assocPath(['suggester', 'highlightedPatchPath'], null),
        R.assocPath(['suggester', 'placePosition'], null)
      )(state);
    case HIGHLIGHT_SUGGESTER_ITEM:
      return R.assocPath(['suggester', 'highlightedPatchPath'], action.payload.patchPath, state);
    case DEBUG_SESSION_STARTED: {
      const currentTab = getTabById(state.currentTabId, state);
      const currentPatchPath = currentTab.patchPath;
      const currentOffset = currentTab.offset;
      return R.compose(
        R.assoc('currentTabId', 'debugger'),
        R.assoc('mode', EDITOR_MODE.DEBUGGING),
        setTabOffset(currentOffset, 'debugger'),
        addTabWithProps('debugger', TAB_TYPES.DEBUGGER, currentPatchPath)
      )(state);
    }
    case DEBUG_SESSION_STOPPED:
      return R.when(
        isTabOpened('debugger'),
        closeTabById('debugger')
      )(state);
    default:
      return state;
  }
};

export default editorReducer;
<commit_msg>fix(xod-client): update patchPath in tabs when renaming patch<commit_after>import R from 'ramda';
import shortid from 'shortid';
import * as XP from 'xod-project';

import {
  EDITOR_DESELECT_ALL,
  EDITOR_SELECT_ENTITY,
  EDITOR_DESELECT_ENTITY,
  EDITOR_ADD_ENTITY_TO_SELECTION,
  EDITOR_SELECT_PIN,
  EDITOR_DESELECT_PIN,
  EDITOR_SET_SELECTED_NODETYPE,
  EDITOR_SWITCH_PATCH,
  EDITOR_SWITCH_TAB,
  TAB_CLOSE,
  TAB_SORT,
  SET_CURRENT_PATCH_OFFSET,
  TOGGLE_HELPBAR,
  SET_FOCUSED_AREA,
  SHOW_SUGGESTER,
  HIDE_SUGGESTER,
  HIGHLIGHT_SUGGESTER_ITEM,
  START_DRAGGING_PATCH,
  PASTE_ENTITIES,
} from './actionTypes';
import {
  PROJECT_CREATE,
  PROJECT_OPEN,
  PROJECT_IMPORT,
  PROJECT_OPEN_WORKSPACE,
  PATCH_ADD,
  PATCH_DELETE,
  PATCH_RENAME,
  NODE_ADD,
  LINK_ADD,
  BULK_DELETE_ENTITIES,
} from '../project/actionTypes';
import {
  DEBUG_SESSION_STARTED,
  DEBUG_SESSION_STOPPED,
} from '../debugger/actionTypes';

import { DEFAULT_PANNING_OFFSET } from '../project/nodeLayout';
import { TAB_TYPES, EDITOR_MODE, SELECTION_ENTITY_TYPE } from './constants';
import { getTabByPatchPath } from './selectors';
import { switchPatchUnsafe } from './actions';

// =============================================================================
//
// Utils
//
// =============================================================================

const getTabs = R.prop('tabs');

const getTabById = R.curry(
  (tabId, state) => R.compose(
    R.propOr(null, tabId),
    getTabs
  )(state)
);

const isTabOpened = R.curry(
  (tabId, state) => R.compose(
    R.complement(R.isNil),
    getTabById(tabId)
  )(state)
);

const isPatchOpened = R.curry(
  (patchPath, state) => R.compose(
    R.complement(R.isNil),
    getTabByPatchPath(patchPath),
    getTabs
  )(state)
);

const setTabOffset = R.curry(
  (offset, tabId, state) => R.assocPath(
    ['tabs', tabId, 'offset'],
    offset,
    state
  )
);

const getTabIdbyPatchPath = R.curry(
  (patchPath, state) => R.compose(
    R.propOr(null, 'id'),
    R.find(R.propEq('patchPath', patchPath)),
    R.values,
    getTabs
  )(state)
);

const listTabsByPatchPath = R.curry(
  (patchPath, state) => R.compose(
    R.filter(R.propEq('patchPath', patchPath)),
    R.values,
    getTabs
  )(state)
);

const syncTabOffset = R.curry(
  (offset, state) => {
    const currentTab = getTabById(state.currentTabId, state);
    const currentPatchPath = currentTab.patchPath;
    if (!currentTab) return state;

    const tabIdsToSync = R.compose(
      R.map(R.prop('id')),
      R.reject(R.propEq('id', state.currentTabId)),
      listTabsByPatchPath(currentPatchPath)
    )(state);
    const syncOffsets = R.map(setTabOffset(offset), tabIdsToSync);

    return R.reduce((acc, fn) => fn(acc), state, syncOffsets);
  }
);

const addTabWithProps = R.curry(
  (id, type, patchPath, state) => {
    const tabs = R.prop('tabs')(state);
    const lastIndex = R.reduce(
      (acc, tab) => R.pipe(
        R.prop('index'),
        R.max(acc)
      )(tab),
      -1,
      R.values(tabs)
    );
    const newIndex = R.inc(lastIndex);

    return R.assocPath(['tabs', id], {
      id,
      patchPath,
      index: newIndex,
      type,
      offset: DEFAULT_PANNING_OFFSET,
    }, state);
  }
);

const addPatchTab = R.curry((newId, patchPath, state) => {
  if (!patchPath) return state;
  return addTabWithProps(newId, TAB_TYPES.PATCH, patchPath, state);
});

const applyTabSort = (tab, payload) => {
  if (R.not(R.has(tab.id, payload))) { return tab; }

  return R.assoc('index', payload[tab.id].index, tab);
};

const resetCurrentPatchPath = (reducer, state, payload) => {
  const newState = R.assoc('tabs', {}, state);
  const firstPatchPath = R.pipe(
    XP.listLocalPatches,
    R.head,
    R.ifElse(
      R.isNil,
      R.always(null),
      XP.getPatchPath
    )
  )(payload);

  return reducer(newState, switchPatchUnsafe(firstPatchPath));
};

const clearSelection = R.flip(R.merge)({
  selection: [],
  linkingPin: null,
});

const openPatchByPath = R.curry(
  (patchPath, state) => {
    const alreadyOpened = isPatchOpened(patchPath, state);
    const tabId = (alreadyOpened) ?
        getTabIdbyPatchPath(patchPath, state) :
        shortid.generate();

    return R.compose(
      R.assoc('currentTabId', tabId),
      clearSelection,
      R.unless(
        () => alreadyOpened,
        addPatchTab(tabId, patchPath)
      )
    )(state);
  }
);

const openLatestOpenedTab = R.converge(
  R.assoc('currentTabId'),
  [
    R.compose( // get patch id from last of remaining tabs
      R.propOr(null, 'id'),
      R.last,
      R.values,
      R.prop('tabs')
    ),
    R.identity,
  ]
);

const closeTabById = R.curry(
  (tabId, state) => {
    const tabToClose = getTabById(tabId, state);

    const isDebuggerTabClosing = () => (tabToClose.type === TAB_TYPES.DEBUGGER);
    const isCurrentTabClosing = R.propEq('currentTabId', tabId);
    const isCurrentDebuggerTabClosing = R.both(
      isDebuggerTabClosing,
      isCurrentTabClosing
    );

    const openOriginalPatch = patchPath => R.compose(
      R.converge(
        setTabOffset(tabToClose.offset),
        [
          getTabIdbyPatchPath(patchPath),
          R.identity,
        ]
      ),
      openPatchByPath(patchPath)
    );

    return R.compose(
      R.cond([
        [isCurrentDebuggerTabClosing, openOriginalPatch(tabToClose.patchPath)],
        [isCurrentTabClosing, openLatestOpenedTab],
        [R.T, R.identity],
      ]),
      R.dissocPath(['tabs', tabId])
    )(state);
  }
);

const closeTabByPatchPath = (patchPath, state) => {
  const tabIdToClose = getTabIdbyPatchPath(patchPath, state);
  return closeTabById(tabIdToClose, state);
};

const renamePatchInTabs = (newPatchPath, oldPatchPath, state) => {
  const tabIdToRename = getTabIdbyPatchPath(oldPatchPath, state);

  return (tabIdToRename === null)
    ? state
    : R.assocPath(['tabs', tabIdToRename, 'patchPath'], newPatchPath, state);
};

const createSelectionEntity = R.curry((entityType, id) => ({ entity: entityType, id }));

// =============================================================================
//
// Reducer
//
// =============================================================================

const editorReducer = (state = {}, action) => {
  switch (action.type) {
    case BULK_DELETE_ENTITIES:
    case EDITOR_DESELECT_ALL:
      return clearSelection(state);
    case EDITOR_SELECT_ENTITY:
      return R.assoc(
        'selection',
        [
          createSelectionEntity(
            action.payload.entityType,
            action.payload.id
          ),
        ],
        state
      );
    case EDITOR_DESELECT_ENTITY:
      return R.over(
        R.lensProp('selection'),
        R.reject(R.equals(createSelectionEntity(
          action.payload.entityType,
          action.payload.id
        ))),
        state
      );
    case EDITOR_ADD_ENTITY_TO_SELECTION:
      return R.over(
        R.lensProp('selection'),
        R.compose(
          R.uniq,
          R.append(createSelectionEntity(
            action.payload.entityType,
            action.payload.id
          ))
        ),
        state
      );
    case PASTE_ENTITIES: {
      const { entities } = action.payload;

      const newSelection = R.compose(
        R.unnest,
        R.values,
        R.evolve({
          nodes: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.NODE),
            XP.getNodeId
          )),
          comments: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.COMMENT),
            XP.getCommentId
          )),
          links: R.map(R.compose(
            createSelectionEntity(SELECTION_ENTITY_TYPE.LINK),
            XP.getLinkId
          )),
        })
      )(entities);

      return R.assoc(
        'selection',
        newSelection,
        state
      );
    }
    case EDITOR_SELECT_PIN:
      return R.assoc('linkingPin', action.payload, state);
    case EDITOR_DESELECT_PIN:
    case LINK_ADD:
      return R.assoc('linkingPin', null, state);
    case START_DRAGGING_PATCH:
      return R.merge(
        state,
        {
          mode: EDITOR_MODE.ACCEPTING_DRAGGED_PATCH,
          draggedPreviewSize: action.payload,
        }
      );
    case NODE_ADD:
      return R.assoc('draggedPreviewSize', { width: 0, height: 0 }, state);
    case EDITOR_SET_SELECTED_NODETYPE:
      return R.assoc('selectedNodeType', action.payload.id, state);
    case PROJECT_CREATE: {
      const newState = R.assoc('tabs', {}, state);
      return editorReducer(newState, switchPatchUnsafe(action.payload.mainPatchPath));
    }
    case PROJECT_OPEN:
    case PROJECT_IMPORT: {
      const newState = R.merge(state, {
        currentTabId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
      return resetCurrentPatchPath(editorReducer, newState, action.payload);
    }
    case PROJECT_OPEN_WORKSPACE:
      return R.merge(state, {
        currentTabId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
    case PATCH_ADD:
    case EDITOR_SWITCH_PATCH:
      return openPatchByPath(action.payload.patchPath, state);
    case EDITOR_SWITCH_TAB:
      return R.assoc(
        'currentTabId',
        action.payload.tabId,
        state
      );
    case PATCH_RENAME:
      return renamePatchInTabs(
        action.payload.newPatchPath,
        action.payload.oldPatchPath,
        state
      );
    case PATCH_DELETE:
      return closeTabByPatchPath(action.payload.patchPath, state);
    case TAB_CLOSE:
      return closeTabById(action.payload.id, state);
    case TAB_SORT:
      return R.assoc(
        'tabs',
        R.reduce(
          (p, cur) => R.assoc(cur.id, applyTabSort(cur, action.payload), p),
          {},
          R.values(state.tabs)
        ),
        state
      );
    case SET_CURRENT_PATCH_OFFSET: {
      return R.compose(
        syncTabOffset(action.payload),
        setTabOffset(action.payload, state.currentTabId)
      )(state);
    }
    case TOGGLE_HELPBAR:
      return R.over(R.lensProp('isHelpbarVisible'), R.not, state);
    case SET_FOCUSED_AREA:
      return R.assoc('focusedArea', action.payload, state);
    case SHOW_SUGGESTER: {
      if (R.path(['suggester', 'visible'], state) === true) return state;

      return R.compose(
        R.assocPath(['suggester', 'visible'], true),
        R.assocPath(['suggester', 'placePosition'], action.payload)
      )(state);
    }
    case HIDE_SUGGESTER:
      return R.compose(
        R.assocPath(['suggester', 'visible'], false),
        R.assocPath(['suggester', 'highlightedPatchPath'], null),
        R.assocPath(['suggester', 'placePosition'], null)
      )(state);
    case HIGHLIGHT_SUGGESTER_ITEM:
      return R.assocPath(['suggester', 'highlightedPatchPath'], action.payload.patchPath, state);
    case DEBUG_SESSION_STARTED: {
      const currentTab = getTabById(state.currentTabId, state);
      const currentPatchPath = currentTab.patchPath;
      const currentOffset = currentTab.offset;
      return R.compose(
        R.assoc('currentTabId', 'debugger'),
        R.assoc('mode', EDITOR_MODE.DEBUGGING),
        setTabOffset(currentOffset, 'debugger'),
        addTabWithProps('debugger', TAB_TYPES.DEBUGGER, currentPatchPath)
      )(state);
    }
    case DEBUG_SESSION_STOPPED:
      return R.when(
        isTabOpened('debugger'),
        closeTabById('debugger')
      )(state);
    default:
      return state;
  }
};

export default editorReducer;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import InputMask from 'react-input-mask';
import classnames from 'classnames';
import omit from 'lodash/omit';

import Icon from 'atoms/Icon';
import Tooltip from 'atoms/Tooltip';
import ErrorMessage from 'atoms/ErrorMessage';

import styles from './text_field.module.scss';

function renderTooltip(tooltip, className) {
  if (typeof tooltip === 'string') {
    return <Tooltip className={styles[className]}>{tooltip}</Tooltip>;
  }

  return <Tooltip className={styles[className]} onClick={tooltip} />;
}

function TextField( props ) {
  const {
    className,
    htmlFor,
    input,
    label,
    mask,
    maskChar,
    meta,
    noBaseStyle,
    placeholder,
    secure,
    id,
    tooltip,
    inputTooltip,
    type,
    maskVal,
    prefix,
  } = props;

  const classes = [
    !noBaseStyle && styles['text-field'],
    meta && meta.active && !noBaseStyle && styles['focused'],
    meta && meta.touched && meta.error && !meta.active && styles['hasError'],
    className,
  ];

  const message = meta && meta.touched && !meta.active && (meta.error || meta.warning);

  return (

        { label &&

            { label }
            { secure && <Icon className={styles['icon-lock']} icon='lock' /> }
            { tooltip && renderTooltip(tooltip, 'tooltip') }

        }

          { mask ?
            <InputMask
              className={styles['input']}
              type='text'
              placeholder={placeholder}
              mask={mask}
              maskChar={maskChar}
              id={id}
              {...input}
            />
            :
            <input
              className={styles['input']}
              type={type}
              placeholder={placeholder}
              id={id}
              value={maskVal(input.value)}
              {...omit(input, [ 'value' ])}
            />
          }
          {inputTooltip && renderTooltip(inputTooltip, 'input-tooltip')}

      <ErrorMessage
        condition={!!message}
        message={message}
        type={meta && meta.warning ? 'warning' : 'error'}
      />

  );
}

TextField.propTypes = {
  /**
   * Label is optional. If not provided, component will reorganize accordingly.
   */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  /**
   * `for` prop on label
   */
  htmlFor: PropTypes.string,

  /**
   * Will append new classname to classSet
   */
  className: PropTypes.string,

  /**
   * placeholder text for text field
   */
  placeholder: PropTypes.string,

  /**
   * object with all necessary props for input
   */
  input: PropTypes.object,
  /**
   * The props under the meta key are metadata about the state of this field that `redux-form` tracks.
   */
  meta: PropTypes.object,
  /**
   * Passing `noBaseStyle=true` will omit the base class style
   * Fields may at times be wrapped up as a collection. In this case, the wrapper would control the styling for those fields as a group.
   */
  noBaseStyle: PropTypes.bool,
  /**
   * Mask the input
   */
  mask: PropTypes.string,

  /**
   * Specific the maskChar (must have `mask` set for this to be applied)
   */
  maskChar: PropTypes.string,

  /**
   * Function to mask the actual input value
   *
   * Provides for more specific customization over the `mask` prop
   */
  maskVal: PropTypes.func,

  /**
   * Adds a lock to the label.
   */
  secure: PropTypes.bool,

  /**
   * id added to the `input` node
   */
  id: PropTypes.string,
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the label
   */
  tooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the input
   */
  inputTooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),

  /**
   * HTML type for input node; when used with `mask` it always defaults to 'text'
   */
  type: PropTypes.oneOf([
    'text',
    'password',
  ]),

  /**
   * provides a prefix for the input
   */
  prefix: PropTypes.oneOf([
    '$',
  ]),
};

TextField.defaultProps = {
  placeholder: 'Placeholder',
  errorMessage: false,
  type: 'text',
  maskVal: val => val,
};

export default TextField;
<commit_msg>Allow for active validation in TextField<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import InputMask from 'react-input-mask';
import classnames from 'classnames';
import omit from 'lodash/omit';

import Icon from 'atoms/Icon';
import Tooltip from 'atoms/Tooltip';
import ErrorMessage from 'atoms/ErrorMessage';

import styles from './text_field.module.scss';

function renderTooltip(tooltip, className) {
  if (typeof tooltip === 'string') {
    return <Tooltip className={styles[className]}>{tooltip}</Tooltip>;
  }

  return <Tooltip className={styles[className]} onClick={tooltip} />;
}

function TextField( props ) {
  const {
    className,
    htmlFor,
    input,
    label,
    mask,
    maskChar,
    meta,
    noBaseStyle,
    placeholder,
    secure,
    id,
    tooltip,
    inputTooltip,
    type,
    maskVal,
    prefix,
    activeValidation,
  } = props;

  const classes = [
    !noBaseStyle && styles['text-field'],
    meta && meta.active && !noBaseStyle && styles['focused'],
    meta && meta.touched && meta.error && !meta.active && styles['hasError'],
    className,
  ];

  let message;

  if (activeValidation) {
    message = meta && (meta.dirty || meta.touched) && (meta.error || meta.warning);
  } else {
    message = meta && meta.touched && !meta.active && (meta.error || meta.warning);
  }

  return (

        { label &&

            { label }
            { secure && <Icon className={styles['icon-lock']} icon='lock' /> }
            { tooltip && renderTooltip(tooltip, 'tooltip') }

        }

          { mask ?
            <InputMask
              className={styles['input']}
              type='text'
              placeholder={placeholder}
              mask={mask}
              maskChar={maskChar}
              id={id}
              {...input}
            />
            :
            <input
              className={styles['input']}
              type={type}
              placeholder={placeholder}
              id={id}
              value={maskVal(input.value)}
              {...omit(input, [ 'value' ])}
            />
          }
          {inputTooltip && renderTooltip(inputTooltip, 'input-tooltip')}

      <ErrorMessage
        condition={!!message}
        message={message}
        type={meta && meta.warning ? 'warning' : 'error'}
      />

  );
}

TextField.propTypes = {
  /**
   * set to true to allow validation errors/warnings to occur `onChange`
   */
  activeValidation: PropTypes.bool,

  /**
   * Label is optional. If not provided, component will reorganize accordingly.
   */
  label: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  /**
   * `for` prop on label
   */
  htmlFor: PropTypes.string,

  /**
   * Will append new classname to classSet
   */
  className: PropTypes.string,

  /**
   * placeholder text for text field
   */
  placeholder: PropTypes.string,

  /**
   * object with all necessary props for input
   */
  input: PropTypes.object,
  /**
   * The props under the meta key are metadata about the state of this field that `redux-form` tracks.
   */
  meta: PropTypes.object,
  /**
   * Passing `noBaseStyle=true` will omit the base class style
   * Fields may at times be wrapped up as a collection. In this case, the wrapper would control the styling for those fields as a group.
   */
  noBaseStyle: PropTypes.bool,
  /**
   * Mask the input
   */
  mask: PropTypes.string,

  /**
   * Specific the maskChar (must have `mask` set for this to be applied)
   */
  maskChar: PropTypes.string,

  /**
   * Function to mask the actual input value
   *
   * Provides for more specific customization over the `mask` prop
   */
  maskVal: PropTypes.func,

  /**
   * Adds a lock to the label.
   */
  secure: PropTypes.bool,

  /**
   * id added to the `input` node
   */
  id: PropTypes.string,
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the label
   */
  tooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),
  /**
   * either a handler for clicking the tooltip, or text to go in the tooltip for the input
   */
  inputTooltip: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
  ]),

  /**
   * HTML type for input node; when used with `mask` it always defaults to 'text'
   */
  type: PropTypes.oneOf([
    'text',
    'password',
  ]),

  /**
   * provides a prefix for the input
   */
  prefix: PropTypes.oneOf([
    '$',
  ]),
};

TextField.defaultProps = {
  placeholder: 'Placeholder',
  errorMessage: false,
  type: 'text',
  maskVal: val => val,
};

export default TextField;
<|endoftext|>