<commit_before>describe('index', function() {

  var sinon = require('sinon'),
      should = require('should'),
      mockery = require('mockery');

  var stubbedWinston = {
    transports: {},
    Transport: function() {}
  };
  var stubbedAWS = {
    CloudWatchLogs: function(options) {
      this.fakeOptions = options;
    },
    config: {
      update: sinon.stub()
    }
  };
  var stubbedCloudwatchIntegration = {
    upload: sinon.spy(function(aws, groupName, streamName, logEvents, retention, cb) {
      this.lastLoggedEvents = logEvents.splice(0, 20);
      cb();
    })
  };
  var clock = sinon.useFakeTimers();

  var WinstonCloudWatch;

  before(function() {
    mockery.enable({
      warnOnReplace: false
    });
    mockery.registerAllowable('util');
    mockery.registerAllowable('lodash.find');
    mockery.registerAllowable('lodash.assign');
    mockery.registerAllowable('lodash.isempty');
    mockery.registerAllowable('lodash.iserror');
    mockery.registerAllowable('./lib/utils');

    mockery.registerMock('proxy-agent', function() { return 'fake' });
    mockery.registerMock('winston', stubbedWinston);
    mockery.registerMock('aws-sdk', stubbedAWS);
    mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);

    mockery.registerAllowable('../index.js');
    WinstonCloudWatch = require('../index.js');
  });

  after(function() {
    mockery.deregisterAll();
    mockery.disable();
  });

  describe('construtor', function() {

    it('allows awsOptions', function() {
      var options = {
        awsOptions: {
          region: 'us-east-1'
        }
      };
      var transport = new WinstonCloudWatch(options);
      transport.cloudwatchlogs.fakeOptions.region.should.equal('us-east-1');
    });

    it('merges awsOptions into existing ones', function() {
      var options = {
        region: 'eu-west-1',
        awsOptions: {
          region: 'us-east-1'
        }
      };
      var transport = new WinstonCloudWatch(options);
      transport.cloudwatchlogs.fakeOptions.region.should.equal('us-east-1');
    });

    it('configures httpOptions if a proxyServer has been defined', function() {
      var options = {
        awsOptions: {
          region: 'us-east-1'
        },
        proxyServer: 'http://test.com'
      };
      var transport = new WinstonCloudWatch(options);
      stubbedAWS.config.update.calledOnce.should.equal(true);
      stubbedAWS.config.update.args[0][0].httpOptions.agent.should.equal('fake');
    });

  });

  describe('log', function() {

    var transport;

    beforeEach(function(done) {
      transport = new WinstonCloudWatch({});
      transport.log({ level: 'level' }, function() {
        clock.tick(2000);
        done();
      });
    });

    it('does not upload if empty message', function(done) {
      stubbedCloudwatchIntegration.upload.called.should.equal(false);
      done();
    });

    it('flushes logs and exits in case of an exception', function(done) {
      transport = new WinstonCloudWatch({});
      transport.log({ message: 'uncaughtException: ' }, function() {
        clock.tick(2000);
        should.not.exist(transport.intervalId);
        // if done is called it means submit(callback) has been called
        done();
      });

    });

    describe('as json', function() {

      var transport;
      var options = {
        jsonMessage: true
      };

      before(function(done) {
        transport = new WinstonCloudWatch(options);
        transport.log({ level: 'level', message: 'message', something: 'else' },
          function() {
            clock.tick(2000);
            done();
          });
      });

      it('logs json', function() {
        var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
        var jsonMessage = JSON.parse(message);
        jsonMessage.level.should.equal('level');
	jsonMessage.message.should.equal('message');
	jsonMessage.something.should.equal('else');
      });
    });

    describe('as text', function() {

      var transport;

      describe('using the default formatter', function() {
        var options = {};
        before(function(done) {
          transport = new WinstonCloudWatch(options);
          transport.log({ level: 'level', message: 'message' }, done);
          clock.tick(2000);
        });

        it('logs text', function() {
          var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
          message.should.equal('level - message');
        });
      });

      describe('using a custom formatter', function() {

        var options = {
          messageFormatter: function(log) {
            return log.level + ' ' + log.message + ' ' + log.something;
          }
        };

        before(function(done) {
          transport = new WinstonCloudWatch(options);
	  transport.log({ level: 'level', message: 'message', something: 'else' }, done);
          clock.tick(2000);
        });

        it('logs text', function() {
          var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
          message.should.equal('level message else');
        });
      });
    });

    describe('info object and a callback as arguments', function() {
      before(function(done) {
        transport = new WinstonCloudWatch({});
        transport.log({ level: 'level', message: 'message' }, function() {
          clock.tick(2000);
          done();
        });
      });

      it('logs text', function() {
        var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
        message.should.equal('level - message');
      });
    });

    describe('handles error', function() {

      beforeEach(function() {
        stubbedCloudwatchIntegration.upload = sinon.stub().yields('ERROR');
        mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);
        sinon.stub(console, 'error');
      });

      afterEach(function() {
        stubbedCloudwatchIntegration = {
          upload: sinon.spy()
        };
        console.error.restore();
      });

      it('invoking errorHandler if provided', function() {
        var errorHandlerSpy = sinon.spy();
        var transport = new WinstonCloudWatch({
          errorHandler: errorHandlerSpy
        });
        transport.log({ level: 'level', message: 'message' }, sinon.stub());
        clock.tick(2000);
        errorHandlerSpy.args[0][0].should.equal('ERROR');
      });

      it('console.error if errorHandler is not provided', function() {
        var transport = new WinstonCloudWatch({});
        transport.log({ level: 'level', message: 'message' }, sinon.stub());
        clock.tick(2000);
        console.error.args[0][0].should.equal('ERROR');
      });

    });
  });

  describe('ktxhbye', function() {

    var transport;

    beforeEach(function() {
      sinon.stub(global, 'setInterval');
      sinon.stub(global, 'clearInterval');
      transport = new WinstonCloudWatch({});
      sinon.stub(transport, 'submit').callsFake(function(cb){
        this.logEvents.splice(0, 20);
        cb();
      });
    });

    afterEach(function() {
      global.setInterval.restore();
      global.clearInterval.restore();
      transport.submit.restore();
    });

    it('clears the interval', function(done) {
      transport.intervalId = 'fake';

      transport.kthxbye(function() {
        global.clearInterval.callCount.should.equal(1);
        should.not.exist(transport.intervalId);
        done();
      });
    });

    it('submit the logs', function(done) {
      transport.kthxbye(function() {
        transport.submit.callCount.should.equal(1);
        done();
      });
    });

    it('should not send all messages if called while posting', function(done) {
      for (let index = 0; index < 30; index++) {
        transport.add({ message: 'message' + index });
      }

      transport.kthxbye(function() {
        transport.logEvents.length.should.equal(0);
        done();
      });
    });
  });

});
<commit_msg>let to var<commit_after>describe('index', function() {

  var sinon = require('sinon'),
      should = require('should'),
      mockery = require('mockery');

  var stubbedWinston = {
    transports: {},
    Transport: function() {}
  };
  var stubbedAWS = {
    CloudWatchLogs: function(options) {
      this.fakeOptions = options;
    },
    config: {
      update: sinon.stub()
    }
  };
  var stubbedCloudwatchIntegration = {
    upload: sinon.spy(function(aws, groupName, streamName, logEvents, retention, cb) {
      this.lastLoggedEvents = logEvents.splice(0, 20);
      cb();
    })
  };
  var clock = sinon.useFakeTimers();

  var WinstonCloudWatch;

  before(function() {
    mockery.enable({
      warnOnReplace: false
    });
    mockery.registerAllowable('util');
    mockery.registerAllowable('lodash.find');
    mockery.registerAllowable('lodash.assign');
    mockery.registerAllowable('lodash.isempty');
    mockery.registerAllowable('lodash.iserror');
    mockery.registerAllowable('./lib/utils');

    mockery.registerMock('proxy-agent', function() { return 'fake' });
    mockery.registerMock('winston', stubbedWinston);
    mockery.registerMock('aws-sdk', stubbedAWS);
    mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);

    mockery.registerAllowable('../index.js');
    WinstonCloudWatch = require('../index.js');
  });

  after(function() {
    mockery.deregisterAll();
    mockery.disable();
  });

  describe('construtor', function() {

    it('allows awsOptions', function() {
      var options = {
        awsOptions: {
          region: 'us-east-1'
        }
      };
      var transport = new WinstonCloudWatch(options);
      transport.cloudwatchlogs.fakeOptions.region.should.equal('us-east-1');
    });

    it('merges awsOptions into existing ones', function() {
      var options = {
        region: 'eu-west-1',
        awsOptions: {
          region: 'us-east-1'
        }
      };
      var transport = new WinstonCloudWatch(options);
      transport.cloudwatchlogs.fakeOptions.region.should.equal('us-east-1');
    });

    it('configures httpOptions if a proxyServer has been defined', function() {
      var options = {
        awsOptions: {
          region: 'us-east-1'
        },
        proxyServer: 'http://test.com'
      };
      var transport = new WinstonCloudWatch(options);
      stubbedAWS.config.update.calledOnce.should.equal(true);
      stubbedAWS.config.update.args[0][0].httpOptions.agent.should.equal('fake');
    });

  });

  describe('log', function() {

    var transport;

    beforeEach(function(done) {
      transport = new WinstonCloudWatch({});
      transport.log({ level: 'level' }, function() {
        clock.tick(2000);
        done();
      });
    });

    it('does not upload if empty message', function(done) {
      stubbedCloudwatchIntegration.upload.called.should.equal(false);
      done();
    });

    it('flushes logs and exits in case of an exception', function(done) {
      transport = new WinstonCloudWatch({});
      transport.log({ message: 'uncaughtException: ' }, function() {
        clock.tick(2000);
        should.not.exist(transport.intervalId);
        // if done is called it means submit(callback) has been called
        done();
      });

    });

    describe('as json', function() {

      var transport;
      var options = {
        jsonMessage: true
      };

      before(function(done) {
        transport = new WinstonCloudWatch(options);
        transport.log({ level: 'level', message: 'message', something: 'else' },
          function() {
            clock.tick(2000);
            done();
          });
      });

      it('logs json', function() {
        var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
        var jsonMessage = JSON.parse(message);
        jsonMessage.level.should.equal('level');
	jsonMessage.message.should.equal('message');
	jsonMessage.something.should.equal('else');
      });
    });

    describe('as text', function() {

      var transport;

      describe('using the default formatter', function() {
        var options = {};
        before(function(done) {
          transport = new WinstonCloudWatch(options);
          transport.log({ level: 'level', message: 'message' }, done);
          clock.tick(2000);
        });

        it('logs text', function() {
          var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
          message.should.equal('level - message');
        });
      });

      describe('using a custom formatter', function() {

        var options = {
          messageFormatter: function(log) {
            return log.level + ' ' + log.message + ' ' + log.something;
          }
        };

        before(function(done) {
          transport = new WinstonCloudWatch(options);
	  transport.log({ level: 'level', message: 'message', something: 'else' }, done);
          clock.tick(2000);
        });

        it('logs text', function() {
          var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
          message.should.equal('level message else');
        });
      });
    });

    describe('info object and a callback as arguments', function() {
      before(function(done) {
        transport = new WinstonCloudWatch({});
        transport.log({ level: 'level', message: 'message' }, function() {
          clock.tick(2000);
          done();
        });
      });

      it('logs text', function() {
        var message = stubbedCloudwatchIntegration.lastLoggedEvents[0].message;
        message.should.equal('level - message');
      });
    });

    describe('handles error', function() {

      beforeEach(function() {
        stubbedCloudwatchIntegration.upload = sinon.stub().yields('ERROR');
        mockery.registerMock('./lib/cloudwatch-integration', stubbedCloudwatchIntegration);
        sinon.stub(console, 'error');
      });

      afterEach(function() {
        stubbedCloudwatchIntegration = {
          upload: sinon.spy()
        };
        console.error.restore();
      });

      it('invoking errorHandler if provided', function() {
        var errorHandlerSpy = sinon.spy();
        var transport = new WinstonCloudWatch({
          errorHandler: errorHandlerSpy
        });
        transport.log({ level: 'level', message: 'message' }, sinon.stub());
        clock.tick(2000);
        errorHandlerSpy.args[0][0].should.equal('ERROR');
      });

      it('console.error if errorHandler is not provided', function() {
        var transport = new WinstonCloudWatch({});
        transport.log({ level: 'level', message: 'message' }, sinon.stub());
        clock.tick(2000);
        console.error.args[0][0].should.equal('ERROR');
      });

    });
  });

  describe('ktxhbye', function() {

    var transport;

    beforeEach(function() {
      sinon.stub(global, 'setInterval');
      sinon.stub(global, 'clearInterval');
      transport = new WinstonCloudWatch({});
      sinon.stub(transport, 'submit').callsFake(function(cb){
        this.logEvents.splice(0, 20);
        cb();
      });
    });

    afterEach(function() {
      global.setInterval.restore();
      global.clearInterval.restore();
      transport.submit.restore();
    });

    it('clears the interval', function(done) {
      transport.intervalId = 'fake';

      transport.kthxbye(function() {
        global.clearInterval.callCount.should.equal(1);
        should.not.exist(transport.intervalId);
        done();
      });
    });

    it('submit the logs', function(done) {
      transport.kthxbye(function() {
        transport.submit.callCount.should.equal(1);
        done();
      });
    });

    it('should not send all messages if called while posting', function(done) {
      for (var index = 0; index < 30; index++) {
        transport.add({ message: 'message' + index });
      }

      transport.kthxbye(function() {
        transport.logEvents.length.should.equal(0);
        done();
      });
    });
  });

});
<|endoftext|>


<commit_before>import _ from 'lodash';
import BaseComponent from '../base/Base';

export default class RadioComponent extends BaseComponent {
  static schema(...extend) {
    return BaseComponent.schema({
      type: 'radio',
      inputType: 'radio',
      label: 'Radio',
      key: 'radio',
      values: [{label: '', value: ''}],
      fieldSet: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Radio',
      group: 'basic',
      icon: 'fa fa-dot-circle-o',
      weight: 80,
      documentation: 'http://help.form.io/userguide/#radio',
      schema: RadioComponent.schema()
    };
  }

  get defaultSchema() {
    return RadioComponent.schema();
  }

  elementInfo() {
    const info = super.elementInfo();
    info.type = 'input';
    info.changeEvent = 'click';
    info.attr.class = 'form-check-input';
    return info;
  }

  createInput(container) {
    const inputGroup = this.ce('div', {
      class: 'input-group'
    });
    const labelOnTheTopOrOnTheLeft = this.optionsLabelOnTheTopOrLeft();
    const wrappers = [];

    if (this.component.inputType === 'radio') {
      this.info.attr.name += this.id;
    }

    _.each(this.component.values, (value) => {
      const wrapperClass = `form-check ${this.optionWrapperClass}`;
      const labelWrapper = this.ce('div', {
        class: wrapperClass
      });
      const label = this.ce('label', {
        class: 'control-label form-check-label'
      });

      this.addShortcut(label, value.shortcut);

      // Determine the attributes for this input.
      const inputId = `${this.id}${this.row}-${value.value}`;
      this.info.attr.id = inputId;
      this.info.attr.value = value.value;
      label.setAttribute('for', this.info.attr.id);

      // Create the input.
      const input = this.ce('input');
      _.each(this.info.attr, (value, key) => {
        input.setAttribute(key, value);
      });

      const labelSpan = this.ce('span');
      if (value.label && labelOnTheTopOrOnTheLeft) {
        label.appendChild(labelSpan);
      }

      this.setInputLabelStyle(label);
      this.setInputStyle(input);

      this.addInput(input, label);

      if (value.label) {
        labelSpan.appendChild(this.text(this.addShortcutToLabel(value.label, value.shortcut)));
      }

      if (value.label && !labelOnTheTopOrOnTheLeft) {
        label.appendChild(labelSpan);
      }
      labelWrapper.appendChild(label);

      inputGroup.appendChild(labelWrapper);
      wrappers.push(labelWrapper);
    });
    this.wrappers = wrappers;
    container.appendChild(inputGroup);
    this.errorContainer = container;
  }

  get optionWrapperClass() {
    const inputType = this.component.inputType;
    const wrapperClass = (this.component.inline ? `form-check-inline ${inputType}-inline` : inputType);
    return wrapperClass;
  }

  optionsLabelOnTheTopOrLeft() {
    return ['top', 'left'].includes(this.component.optionsLabelPosition);
  }

  optionsLabelOnTheTopOrBottom() {
    return ['top', 'bottom'].includes(this.component.optionsLabelPosition);
  }

  setInputLabelStyle(label) {
    if (this.component.optionsLabelPosition === 'left') {
      _.assign(label.style, {
        textAlign: 'center',
        paddingLeft: 0,
      });
    }

    if (this.optionsLabelOnTheTopOrBottom()) {
      _.assign(label.style, {
        display: 'block',
        textAlign: 'center',
        paddingLeft: 0,
      });
    }
  }

  setInputStyle(input) {
    if (this.component.optionsLabelPosition === 'left') {
      _.assign(input.style, {
        position: 'initial',
        marginLeft: '7px'
      });
    }

    if (this.optionsLabelOnTheTopOrBottom()) {
      _.assign(input.style, {
        width: '100%',
        position: 'initial',
        marginLeft: 0
      });
    }
  }

  getValue() {
    if (this.viewOnly) {
      return this.dataValue;
    }
    let value = '';
    _.each(this.inputs, (input) => {
      if (input.checked) {
        value = input.value;
        if (value === 'true') {
          value = true;
        }
        else if (value === 'false') {
          value = false;
        }
        else if (!isNaN(parseInt(value, 10)) && isFinite(value)) {
          value = parseInt(value, 10);
        }
      }
    });
    return value;
  }

  getView(value) {
    if (!value) {
      return '';
    }
    if (!_.isString(value)) {
      return _.toString(value);
    }

    const option = _.find(this.component.values, (v) => v.value === value);

    return _.get(option, 'label');
  }

  setValueAt(index, value) {
    if (this.inputs && this.inputs[index]) {
      let inputValue = this.inputs[index].value;
      if (inputValue === 'true') {
        inputValue = true;
      }
      else if (inputValue === 'false') {
        inputValue = false;
      }
      else if (!isNaN(parseInt(inputValue, 10)) && isFinite(inputValue)) {
        inputValue = parseInt(inputValue, 10);
      }

      this.inputs[index].checked = (inputValue === value);
    }
  }

  updateValue(value, flags) {
    const changed = super.updateValue(value, flags);
    if (changed) {
      //add/remove selected option class
      const value = this.dataValue;
      const optionSelectedClass = 'radio-selected';

      _.each(this.wrappers, (wrapper, index) => {
        const input = this.inputs[index];
        if (input.value === value) {
          //add class to container when selected
          this.addClass(wrapper, optionSelectedClass);
        }
        else {
          this.removeClass(wrapper, optionSelectedClass);
        }
      });
    }
    return changed;
  }

  destroy() {
    super.destroy.apply(this, Array.prototype.slice.apply(arguments));
  }
}
<commit_msg>Fixed the radio and checkbox styling for Bootstrap 4 and also compatible with Bootstrap 3.<commit_after>import _ from 'lodash';
import BaseComponent from '../base/Base';

export default class RadioComponent extends BaseComponent {
  static schema(...extend) {
    return BaseComponent.schema({
      type: 'radio',
      inputType: 'radio',
      label: 'Radio',
      key: 'radio',
      values: [{label: '', value: ''}],
      fieldSet: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Radio',
      group: 'basic',
      icon: 'fa fa-dot-circle-o',
      weight: 80,
      documentation: 'http://help.form.io/userguide/#radio',
      schema: RadioComponent.schema()
    };
  }

  get defaultSchema() {
    return RadioComponent.schema();
  }

  elementInfo() {
    const info = super.elementInfo();
    info.type = 'input';
    info.changeEvent = 'click';
    info.attr.class = 'form-check-input';
    return info;
  }

  createInput(container) {
    const inputGroup = this.ce('div', {
      class: 'form-group'
    });
    const labelOnTheTopOrOnTheLeft = this.optionsLabelOnTheTopOrLeft();
    const wrappers = [];

    if (this.component.inputType === 'radio') {
      this.info.attr.name += this.id;
    }

    _.each(this.component.values, (value) => {
      const wrapperClass = `form-check ${this.optionWrapperClass}`;
      const labelWrapper = this.ce('div', {
        class: wrapperClass
      });
      const label = this.ce('label', {
        class: 'control-label form-check-label'
      });

      this.addShortcut(label, value.shortcut);

      // Determine the attributes for this input.
      const inputId = `${this.id}${this.row}-${value.value}`;
      this.info.attr.id = inputId;
      this.info.attr.value = value.value;
      label.setAttribute('for', this.info.attr.id);

      // Create the input.
      const input = this.ce('input');
      _.each(this.info.attr, (value, key) => {
        input.setAttribute(key, value);
      });

      const labelSpan = this.ce('span');
      if (value.label && labelOnTheTopOrOnTheLeft) {
        label.appendChild(labelSpan);
      }

      this.setInputLabelStyle(label);
      this.setInputStyle(input);

      this.addInput(input, label);

      if (value.label) {
        labelSpan.appendChild(this.text(this.addShortcutToLabel(value.label, value.shortcut)));
      }

      if (value.label && !labelOnTheTopOrOnTheLeft) {
        label.appendChild(labelSpan);
      }
      labelWrapper.appendChild(label);

      inputGroup.appendChild(labelWrapper);
      wrappers.push(labelWrapper);
    });
    this.wrappers = wrappers;
    container.appendChild(inputGroup);
    this.errorContainer = container;
  }

  get optionWrapperClass() {
    const inputType = this.component.inputType;
    const wrapperClass = (this.component.inline ? `form-check-inline ${inputType}-inline` : inputType);
    return wrapperClass;
  }

  optionsLabelOnTheTopOrLeft() {
    return ['top', 'left'].includes(this.component.optionsLabelPosition);
  }

  optionsLabelOnTheTopOrBottom() {
    return ['top', 'bottom'].includes(this.component.optionsLabelPosition);
  }

  setInputLabelStyle(label) {
    if (this.component.optionsLabelPosition === 'left') {
      _.assign(label.style, {
        textAlign: 'center',
        paddingLeft: 0,
      });
    }

    if (this.optionsLabelOnTheTopOrBottom()) {
      _.assign(label.style, {
        display: 'block',
        textAlign: 'center',
        paddingLeft: 0,
      });
    }
  }

  setInputStyle(input) {
    if (this.component.optionsLabelPosition === 'left') {
      _.assign(input.style, {
        position: 'initial',
        marginLeft: '7px'
      });
    }

    if (this.optionsLabelOnTheTopOrBottom()) {
      _.assign(input.style, {
        width: '100%',
        position: 'initial',
        marginLeft: 0
      });
    }
  }

  getValue() {
    if (this.viewOnly) {
      return this.dataValue;
    }
    let value = '';
    _.each(this.inputs, (input) => {
      if (input.checked) {
        value = input.value;
        if (value === 'true') {
          value = true;
        }
        else if (value === 'false') {
          value = false;
        }
        else if (!isNaN(parseInt(value, 10)) && isFinite(value)) {
          value = parseInt(value, 10);
        }
      }
    });
    return value;
  }

  getView(value) {
    if (!value) {
      return '';
    }
    if (!_.isString(value)) {
      return _.toString(value);
    }

    const option = _.find(this.component.values, (v) => v.value === value);

    return _.get(option, 'label');
  }

  setValueAt(index, value) {
    if (this.inputs && this.inputs[index]) {
      let inputValue = this.inputs[index].value;
      if (inputValue === 'true') {
        inputValue = true;
      }
      else if (inputValue === 'false') {
        inputValue = false;
      }
      else if (!isNaN(parseInt(inputValue, 10)) && isFinite(inputValue)) {
        inputValue = parseInt(inputValue, 10);
      }

      this.inputs[index].checked = (inputValue === value);
    }
  }

  updateValue(value, flags) {
    const changed = super.updateValue(value, flags);
    if (changed) {
      //add/remove selected option class
      const value = this.dataValue;
      const optionSelectedClass = 'radio-selected';

      _.each(this.wrappers, (wrapper, index) => {
        const input = this.inputs[index];
        if (input.value === value) {
          //add class to container when selected
          this.addClass(wrapper, optionSelectedClass);
        }
        else {
          this.removeClass(wrapper, optionSelectedClass);
        }
      });
    }
    return changed;
  }

  destroy() {
    super.destroy.apply(this, Array.prototype.slice.apply(arguments));
  }
}
<|endoftext|>


<commit_before>/**
 * test/index.js
 * Contains tests for client entry point.
 *
 * @author Francis Brito <fr.br94@gmail.com>
 * @license MIT
 */
/* global describe, it, before */
/* jshint expr:true */
'use strict';

require('should');

var Client = require('../').Client;

var PH_TEST_SERVER = 'http://private-50d6dc-printhouse.apiary-mock.com/api';

describe('Client', function () {
    describe('constructor', function () {
        it('should throw if no API key is provided.', function () {
            (function () {
               new Client();
            }).should.throw('No API key provided.');
        });

        it('should use default API endpoint if none is provided.', function () {
            var client = new Client('some api key');

            // NOTE: Accessing client's private API, so sort of breaking the rules here.
            client._endpoint.should.eql(Client.DEFAULT_API_URL);
        });

        it('should use latest API version if none is specified.', function () {
            var client = new Client('some api key');

            client._version.should.eql(Client.DEFAULT_API_VERSION);
        });
    });

    describe('#getProducts', function () {
        before(function () {
            this.client = new Client('some api token', {
                endpoint: PH_TEST_SERVER
            });
        });

        it('should return a list of products.');

        it('should throw if API endpoint is not reachable.');

        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getProductById', function () {
        before(function () {
            this.client = new Client('some api token', {
                endpoint: PH_TEST_SERVER
            });
        });

        it('should throw if no product with id `id` is found.');

        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getPrintFiles', function () {
        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getPrintFileById', function () {
        it('should throw if no print file with id `id` is found.');
        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#createPrintFile', function () {
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getOrders', function () {
        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getOrderById', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#createOrder', function () {
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#updateOrder', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');
        it('should throw if order has already being confirmed.');
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#confirmOrder', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');
        it('should throw if order was already confirmed.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });
});

/*
 * Helpers
 */
// function isACanvas(c) { return c.kind === 'Canvas'; }
// function every(array, predicate) { return array.every(predicate); }
<commit_msg>Add get products test .<commit_after>/**
 * test/index.js
 * Contains tests for client entry point.
 *
 * @author Francis Brito <fr.br94@gmail.com>
 * @license MIT
 */
/* global describe, it, before */
/* jshint expr:true */
'use strict';

require('should');

var Client = require('../').Client;

var PH_TEST_SERVER = 'http://private-50d6dc-printhouse.apiary-mock.com/api';

describe('Client', function () {
    describe('constructor', function () {
        it('should throw if no API key is provided.', function () {
            (function () {
               new Client();
            }).should.throw('No API key provided.');
        });

        it('should use default API endpoint if none is provided.', function () {
            var client = new Client('some api key');

            // NOTE: Accessing client's private API, so sort of breaking the rules here.
            client._endpoint.should.eql(Client.DEFAULT_API_URL);
        });

        it('should use latest API version if none is specified.', function () {
            var client = new Client('some api key');

            client._version.should.eql(Client.DEFAULT_API_VERSION);
        });
    });

    describe('#getProducts', function () {
        before(function () {
            this.client = new Client('some api token', {
                endpoint: PH_TEST_SERVER
            });
        });

        it('should return a list of products.', function (next) {
            this.client.getProducts(function (err, products) {
                products.should.be.ok.and.be.an.Array;

                next();
            });
        });

        it('should throw if API endpoint is not reachable.');

        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getProductById', function () {
        before(function () {
            this.client = new Client('some api token', {
                endpoint: PH_TEST_SERVER
            });
        });

        it('should throw if no product with id `id` is found.');

        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getPrintFiles', function () {
        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getPrintFileById', function () {
        it('should throw if no print file with id `id` is found.');
        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#createPrintFile', function () {
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getOrders', function () {
        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#getOrderById', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#createOrder', function () {
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#updateOrder', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');
        it('should throw if order has already being confirmed.');
        it('should throw if no `fields` parameter is provided.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });

    describe('#confirmOrder', function () {
        it('should throw if no order with id `id` is found.');
        it('should throw if no `id` field is provided.');
        it('should throw if order was already confirmed.');

        it('should throw if API endpoint is not reachable.');
        it('should throw if API endpoint responds with a server error.');
    });
});

/*
 * Helpers
 */
// function isACanvas(c) { return c.kind === 'Canvas'; }
// function every(array, predicate) { return array.every(predicate); }
<|endoftext|>


<commit_before>import _ from 'lodash';
import NestedComponent from '../_classes/nested/NestedComponent';

export default class TableComponent extends NestedComponent {
  static emptyTable(numRows, numCols) {
    const rows = [];
    for (let i = 0; i < numRows; i++) {
      const cols = [];
      for (let j = 0; j < numCols; j++) {
        cols.push({ components: [] });
      }
      rows.push(cols);
    }
    return rows;
  }

  static schema(...extend) {
    return NestedComponent.schema({
      type: 'table',
      input: false,
      key: 'table',
      numRows: 3,
      numCols: 3,
      rows: TableComponent.emptyTable(3, 3),
      header: [],
      caption: '',
      striped: false,
      bordered: false,
      hover: false,
      condensed: false,
      persistent: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Table',
      group: 'layout',
      icon: 'table',
      weight: 40,
      documentation: 'http://help.form.io/userguide/#table',
      schema: TableComponent.schema()
    };
  }

  get defaultSchema() {
    return TableComponent.schema();
  }

  get schema() {
    const schema = _.omit(super.schema, 'components');
    schema.rows = [];
    this.eachComponent((component) => {
      if (!schema.rows || !schema.rows.length) {
        schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols);
      }
      if (!schema.rows[component.tableRow]) {
        schema.rows[component.tableRow] = [];
      }
      if (!schema.rows[component.tableRow][component.tableColumn]) {
        schema.rows[component.tableRow][component.column] = { components: [] };
      }
      schema.rows[component.tableRow][component.tableColumn].components.push(component.schema);
    });
    if (!schema.rows.length) {
      schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols);
    }
    return schema;
  }

  get className() {
    return `table-responsive ${super.className}`;
  }

  get tableKey() {
    return `table-${this.key}`;
  }

  constructor(...args) {
    super(...args);
    this.noField = true;
  }

  init() {
    super.init();
    // Ensure component.rows has the correct number of rows and columns.
    for (let rowIndex = 0; rowIndex < this.component.numRows; rowIndex++) {
      this.component.rows[rowIndex] = this.component.rows[rowIndex] || [];
      for (let colIndex = 0; colIndex < this.component.numCols; colIndex++) {
        this.component.rows[rowIndex][colIndex] = this.component.rows[rowIndex][colIndex] || { components: [] };
      }
      this.component.rows[rowIndex] = this.component.rows[rowIndex].slice(0, this.component.numCols);
    }
    this.component.rows = this.component.rows.slice(0, this.component.numRows);

      this.table = [];
    _.each(this.component.rows, (row, rowIndex) => {
      this.table[rowIndex] = [];
      _.each(row, (column, colIndex) => {
        this.table[rowIndex][colIndex] = [];
        _.each(column.components, (comp) => {
          const component = this.createComponent(comp);
          component.tableRow = rowIndex;
          component.tableColumn = colIndex;
          this.table[rowIndex][colIndex].push(component);
        });
      });
    });
  }

  render() {
    return super.render(this.renderTemplate('table', {
      tableKey: this.tableKey,
      tableComponents: this.table.map(row =>
        row.map(column =>
          this.renderComponents(column)
        )
      )
    }));
  }

  attach(element) {
    const keys = this.table.reduce((prev, row, rowIndex) => {
      prev[`${this.tableKey}-${rowIndex}`] = 'multiple';
      return prev;
    }, {});
    this.loadRefs(element, keys);
    super.attach(element);
    this.table.forEach((row, rowIndex) => {
      row.forEach((column, columnIndex) => {
        this.attachComponents(this.refs[`${this.tableKey}-${rowIndex}`][columnIndex], this.table[rowIndex][columnIndex], this.component.rows[rowIndex][columnIndex].components);
      });
    });
  }

  destroy(all) {
    super.destroy(all);
    delete this.table;
  }
}
<commit_msg>Added label for Table component.<commit_after>import _ from 'lodash';
import NestedComponent from '../_classes/nested/NestedComponent';

export default class TableComponent extends NestedComponent {
  static emptyTable(numRows, numCols) {
    const rows = [];
    for (let i = 0; i < numRows; i++) {
      const cols = [];
      for (let j = 0; j < numCols; j++) {
        cols.push({ components: [] });
      }
      rows.push(cols);
    }
    return rows;
  }

  static schema(...extend) {
    return NestedComponent.schema({
      label: 'Table',
      type: 'table',
      input: false,
      key: 'table',
      numRows: 3,
      numCols: 3,
      rows: TableComponent.emptyTable(3, 3),
      header: [],
      caption: '',
      striped: false,
      bordered: false,
      hover: false,
      condensed: false,
      persistent: false
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Table',
      group: 'layout',
      icon: 'table',
      weight: 40,
      documentation: 'http://help.form.io/userguide/#table',
      schema: TableComponent.schema()
    };
  }

  get defaultSchema() {
    return TableComponent.schema();
  }

  get schema() {
    const schema = _.omit(super.schema, 'components');
    schema.rows = [];
    this.eachComponent((component) => {
      if (!schema.rows || !schema.rows.length) {
        schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols);
      }
      if (!schema.rows[component.tableRow]) {
        schema.rows[component.tableRow] = [];
      }
      if (!schema.rows[component.tableRow][component.tableColumn]) {
        schema.rows[component.tableRow][component.column] = { components: [] };
      }
      schema.rows[component.tableRow][component.tableColumn].components.push(component.schema);
    });
    if (!schema.rows.length) {
      schema.rows = TableComponent.emptyTable(this.component.numRows, this.component.numCols);
    }
    return schema;
  }

  get className() {
    return `table-responsive ${super.className}`;
  }

  get tableKey() {
    return `table-${this.key}`;
  }

  constructor(...args) {
    super(...args);
    this.noField = true;
  }

  init() {
    super.init();
    // Ensure component.rows has the correct number of rows and columns.
    for (let rowIndex = 0; rowIndex < this.component.numRows; rowIndex++) {
      this.component.rows[rowIndex] = this.component.rows[rowIndex] || [];
      for (let colIndex = 0; colIndex < this.component.numCols; colIndex++) {
        this.component.rows[rowIndex][colIndex] = this.component.rows[rowIndex][colIndex] || { components: [] };
      }
      this.component.rows[rowIndex] = this.component.rows[rowIndex].slice(0, this.component.numCols);
    }
    this.component.rows = this.component.rows.slice(0, this.component.numRows);

      this.table = [];
    _.each(this.component.rows, (row, rowIndex) => {
      this.table[rowIndex] = [];
      _.each(row, (column, colIndex) => {
        this.table[rowIndex][colIndex] = [];
        _.each(column.components, (comp) => {
          const component = this.createComponent(comp);
          component.tableRow = rowIndex;
          component.tableColumn = colIndex;
          this.table[rowIndex][colIndex].push(component);
        });
      });
    });
  }

  render() {
    return super.render(this.renderTemplate('table', {
      tableKey: this.tableKey,
      tableComponents: this.table.map(row =>
        row.map(column =>
          this.renderComponents(column)
        )
      )
    }));
  }

  attach(element) {
    const keys = this.table.reduce((prev, row, rowIndex) => {
      prev[`${this.tableKey}-${rowIndex}`] = 'multiple';
      return prev;
    }, {});
    this.loadRefs(element, keys);
    super.attach(element);
    this.table.forEach((row, rowIndex) => {
      row.forEach((column, columnIndex) => {
        this.attachComponents(this.refs[`${this.tableKey}-${rowIndex}`][columnIndex], this.table[rowIndex][columnIndex], this.component.rows[rowIndex][columnIndex].components);
      });
    });
  }

  destroy(all) {
    super.destroy(all);
    delete this.table;
  }
}
<|endoftext|>


<commit_before>// jshint esnext:true,node:true
"use strict";

const Lab = require("lab");
const lab = exports.lab = Lab.script();
const plugin = require('../index.js');
const should = require('chai').should();
const sinon = require('sinon');

lab.experiment("hapi-graceful-pm2", () => {
  let server = {};

  let options = {};

  lab.beforeEach((done) => {
    sinon.stub(process, 'on').returns();
    sinon.stub(process, 'exit');
    server.root = {
      stop: sinon.stub()
    };
    server.log = sinon.stub();

    return plugin.register(server, options, done);
  });

  lab.afterEach((done) => {
    process.on.restore();
    process.exit.restore();
    return done();
  });

  lab.test("should have correct attributes", (done) => {
    let pkg = require('../package.json');
    plugin.register.attributes.pkg.should.deep.equal(pkg);
    return done();
  });

  lab.test("should bind to correct process method", (done) => {
    process.on.args[0][0].should.equal('SIGINT');
    return done();
  });

  lab.test("should stop server if shutdown", (done) => {
    server.log.returns();
    server.root.stop.yields();
    process.exit.restore();
    sinon.stub(process, 'exit', function(code) {
      code.should.equal(0);
      server.root.stop.calledOnce.should.be.true;
      return done();
    });

    let method = process.on.args[0][1];
    method('shutdown');
  });

  lab.test("should not stop server if any other message", (done) => {
    let method = process.on.args[0][1];
    method('any other message');
    return done();
  });

});
<commit_msg>update test hapi 17 & stub 3<commit_after>// jshint esnext:true,node:true
"use strict";

const Lab = require("lab");
const lab = exports.lab = Lab.script();
const plugin = require('../index.js').plugin;
const should = require('chai').should();
const sinon = require('sinon');

lab.experiment("hapi-graceful-pm2", () => {
  let server = {};

  let options = {};

  lab.beforeEach(() => {
    sinon.stub(process, 'on').returns();
    sinon.stub(process, 'exit');
    server = {
      stop: sinon.stub()
    };
    server.log = sinon.stub();

    return plugin.register(server, options);
  });

  lab.afterEach(() => {
    process.on.restore();
    process.exit.restore();
  });

  lab.test("should have correct attributes", () => {
    let pkg = require('../package.json');
    plugin.pkg.should.deep.equal(pkg);
  });

  lab.test("should bind to correct process method", () => {
    process.on.args[0][0].should.equal('SIGINT');
  });

  lab.test("should stop server if shutdown", () => {
    server.log.returns();
    server.stop.yields();
    process.exit.restore();
    sinon.stub(process, 'exit').callsFake((code) => {
        code.should.equal(0);
        server.stop.calledOnce.should.be.true;
    });

    let method = process.on.args[0][1];
    method('shutdown');
  });

  lab.test("should not stop server if any other message", () => {
    let method = process.on.args[0][1];
    method('any other message');
  });

});
<|endoftext|>


<commit_before>import React from 'react';
import classNames from 'classnames';
import TableRow from './table-row';
import TableCell from './table-cell';
import TableHeader from './table-header';
import Pager from './../pager';

/**
 * A Table widget.
 *
 * == How to use a Table in a component:
 *
 * In your file:
 *
 *   import { Table, TableRow, TableCell, TableHeader } from 'carbon/lib/components/table';
 *
 * To render a Table:
 *
 *   // map data to table rows
 *   let tableRows = (
 *     this.props.data.map((datum, key) => {
 *       return (
 *         <TableRow>
 *           <TableCell>
 *             { datum.firstName }
 *           </TableCell>
 *
 *           <TableCell>
 *             { datum.lastName }
 *           </TableCell>
 *         </TableRow>
 *       );
 *     });
 *   );
 *
 *   // prepend array of rows with a header row
 *   tableRows.unshift(
 *     <TableRow>
 *       <TableHeader>First Name</TableHeader>
 *       <TableHeader>Last Name</TableHeader>
 *     </TableRow>
 *   );
 *
 *   // render the table with the table rows
 *

 *     { tableRows }
 *

 *
 * == Pagination
 *
 * To add a pagination footer to the table you will need to pass some extra props to the table
 *
 *  let sizeOptions = Immutable.fromJS([{ id: '10', name: 10 }, { id: '25', name: 25 }, { id: '50', name: 50 }]),
 *
 * <Table
 *   paginate={ true }                        // Show the pagination footer
 *   currentPage='1'                          // Required - Current visible page
 *   pageSize='10'                            // Required - Number of records to show per page
 *   totalRecords                             // Required - Total number of records
 *   showPageSizeSelection={ false }          // Options  - Show page size selection
 *   pageSizeSelectionOptions={ sizeOptions } // Optional - Page Size Options
 * />
 *
 * == Sorting
 *
 *  To enable column sorting, you will need to configure the Table Header component.
 * See the Table Header component documentation.
 *
 * @class Table
 * @constructor
 */
class Table extends React.Component {

  /**
   * Maintains the height of the table
   *
   * @property tableHeight
   * @type {Number}
   */
  tableHeight = 0;

  static propTypes = {
    /**
     * Emitted when table component changes e.g.
     * Pager, sorting, filter
     *
     * @property onChange
     * @type {Function}
     */
    onChange: React.PropTypes.func,

    /**
     * Show the pagination footer
     *
     * @property paginate
     * @type {Boolean}
     */
    paginate: React.PropTypes.bool,

    /**
     * Pagination
     * Current Visible Page
     *
     * @property currentPage
     * @type {String}
     */
    currentPage: React.PropTypes.string,

    /**
     * Pagination
     * Page Size of grid (number of visible records)
     *
     * @property pageSize
     * @type {String}
     */
    pageSize: React.PropTypes.string,

    /**
     * Pagination
     * Options for pageSize default - 10, 25, 50
     *
     * @property pageSizeSelectionOptions
     * @type {Object} Immutable
     */
    pageSizeSelectionOptions: React.PropTypes.object,

    /**
     * Pagination
     * Is the page size dropdown visible
     *
     * @property showPageSizeSelection
     * @type {Boolean}
     */
    showPageSizeSelection: React.PropTypes.bool,

    /**
     * Pagination
     * Total number of records in the grid
     *
     * @property totalRecords
     * @type {String}
     */
    totalRecords: React.PropTypes.string
  }

  /**
   * Lifecycle for after mounting
   * Resize the table to set the correct height on pageload
   *
   * @method componentDidMount
   * @return {Void}
   */
  componentDidMount() {
    this.resizeTable();
  }

  static childContextTypes = {
    /**
     * Defines a context object for child components of the table component.
     * https://facebook.github.io/react/docs/context.html
     *
     * @property table
     * @type {Object}
     */
    onSort: React.PropTypes.func,
    sortedColumn: React.PropTypes.string,
    sortOrder: React.PropTypes.string
  }

  /**
   * Returns table object to child components.
   *
   * @method getChildContext
   * @return {void}
   */
  getChildContext = () => {
    return {
      onSort: this.onSort,
      sortedColumn: this.sortedColumn,
      sortOrder: this.sortOrder
    };
  }

  /**
   * Lifecycle for after a update has happened
   * If pageSize has updated to a smaller value - reset table height
   * else resize table
   *
   * @method componentDidUpdate
   * @return {Void}
   */
  componentDidUpdate(prevProps) {
    if (this.shouldResetTableHeight(prevProps)) {
      this.resetTableHeight();
    } else {
      this.resizeTable();
    }
  }

  /**
   * Reset the minHeight and tableHeight of the table
   *
   * @method resetTableHeight
   * @return {Void}
   */
  resetTableHeight() {
    this._wrapper.style.minHeight = '0';
    this.tableHeight = 0;
    setTimeout(() => {
      this.resizeTable();
    }, 0);
  }

  /**
   * Increase the minheight of the table if the new height
   * is greater than the previous
   *
   * @method resizeTable
   * @return {Void}
   */
  resizeTable() {
    if (this._table.offsetHeight > this.tableHeight) {
      this.tableHeight = this._table.offsetHeight;
      this._wrapper.style.minHeight = this.tableHeight + 'px';
    }
  }

  /**
   * Test if the table height should be reset to 0
   *
   * @method shouldResetTableHeight
   * @param prevProps - props before update
   * @return {Boolean}
   */
  shouldResetTableHeight(prevProps) {
    return prevProps.pageSize > this.pageSize;
  }

  /**
   * Get pageSize for table
   *
   * @method pageSize
   * @return {String} table page size
   */
  get pageSize() {
    return this.props.pageSize;
  }

  /**
   * Emit onChange event with options
   * needed to fetch the new data
   *
   * @method emitOnChangeCallback
   * @param {String} element changed element
   * @param {Object} options base and updated options
   * @return {Void}
   */
  emitOnChangeCallback = (element, options) => {
    this.props.onChange(element, options);
  }

  /**
   * Handles when the pager emits a onChange event
   * Passes data to emitOnChangeCallback in the correct
   * format
   *
   * @method onPagination
   * @param {String} currentPage
   * @param {String} pageSize
   * @return {Void}
   */
  onPagination = (currentPage, pageSize) => {
    let options = this.emitOptions;
    options.currentPage = currentPage;
    options.pageSize = pageSize;
    this.emitOnChangeCallback('pager', options);
  }

  /**
   * Returns the currently sorted column.
   *
   * @method sortedColumn
   * @return {String}
   */
  get sortedColumn() {
    return this.props.sortedColumn;
  }

  /**
   * Returns the current sort order.
   *
   * @method sortOrder
   * @return {String}
   */
  get sortOrder() {
    return this.props.sortOrder;
  }

  /**
   * Handles what happens on sort.
   *
   * @method onSort
   * @param {String} sortedColumn
   * @param {String} sortOrder
   */
  onSort = (sortedColumn, sortOrder) => {
    let options = this.emitOptions;
    options.sortedColumn = sortedColumn;
    options.sortOrder = sortOrder;
    this.emitOnChangeCallback('table', options);
  }

  /**
   * Base Options to be emitted by onChange
   *
   * @method emitOptions
   * @return {Object} options to emit
   */
  get emitOptions() {
    return {
      currentPage: this.props.currentPage   || '',
      pageSize: this.props.pageSize         || '',
      sortOrder: this.props.sortOrder       || '',
      sortedColumn: this.props.sortedColumn || ''
    };
  }

  /**
   * Props to pass to pager component
   *
   * @method pagerProps
   * @return {Object} props
   */
  get pagerProps() {
    return {
      currentPage: this.props.currentPage,
      onPagination: this.onPagination,
      pageSize: this.defaultPageSize,
      pageSizeSelectionOptions: this.props.pageSizeSelectionOptions,
      showPageSizeSelection: this.props.showPageSizeSelection,
      totalRecords: this.props.totalRecords
    };
  }

  /**
   * Page size for page load
   *
   * @method defaultPageSize
   * @return {Void}
   */
  get defaultPageSize() {
    if (this.props.pageSize) {
      return this.props.pageSize;
    } else if (this.props.pageSizeSelectionOptions) {
      return this.props.pageSizeSelectionOptions.first().get('id');
    }
    return '10';
  }

  /**
   * Returns the pager if paginate is true
   *
   * @method pager
   * @return {JSX} pager
   */
  get pager() {
    if (this.props.paginate) {
      return (<Pager { ...this.pagerProps } />);
    }
  }

  /**
   * Classes that apply to the parent table div
   *
   * @method mainClasses
   * @return {String}
   */
  get mainClasses() {
    return classNames(
      'ui-table',
      this.props.className
    );
  }

  /**
   * Classes that apply to the table wrapper
   *
   * @method wrapperClasses
   * @return {String}
   */
  get wrapperClasses() {
    return classNames(
      'ui-table__wrapper',
      this.props.className,
      { [`ui-table--pager`]: this.props.paginate }
    );
  }

  /**
   * Classes to apply to the table
   *
   * @method tableClasses
   * @return {String}
   */
  get tableClasses() {
    return 'ui-table__table';
  }

  /**
   * Renders the component.
   *
   * @method render
   */
  render() {
    return (

 { this._wrapper = wrapper; } } >

 { this._table = table; } } >

              { this.props.children }

        { this.pager }

    );
  }
}

export {
  Table,
  TableRow,
  TableCell,
  TableHeader
};
<commit_msg>Update table.js<commit_after>import React from 'react';
import classNames from 'classnames';
import TableRow from './table-row';
import TableCell from './table-cell';
import TableHeader from './table-header';
import Pager from './../pager';

/**
 * A Table widget.
 *
 * == How to use a Table in a component:
 *
 * In your file:
 *
 *   import { Table, TableRow, TableCell, TableHeader } from 'carbon/lib/components/table';
 *
 * To render a Table:
 *
 *   // map data to table rows
 *   let tableRows = (
 *     this.props.data.map((datum, key) => {
 *       return (
 *         <TableRow>
 *           <TableCell>
 *             { datum.firstName }
 *           </TableCell>
 *
 *           <TableCell>
 *             { datum.lastName }
 *           </TableCell>
 *         </TableRow>
 *       );
 *     });
 *   );
 *
 *   // prepend array of rows with a header row
 *   tableRows.unshift(
 *     <TableRow>
 *       <TableHeader>First Name</TableHeader>
 *       <TableHeader>Last Name</TableHeader>
 *     </TableRow>
 *   );
 *
 *   // render the table with the table rows
 *

 *     { tableRows }
 *

 *
 * == Pagination
 *
 * To add a pagination footer to the table you will need to pass some extra props to the table
 *
 *  let sizeOptions = Immutable.fromJS([{ id: '10', name: 10 }, { id: '25', name: 25 }, { id: '50', name: 50 }]),
 *
 * <Table
 *   paginate={ true }                        // Show the pagination footer
 *   currentPage='1'                          // Required - Current visible page
 *   pageSize='10'                            // Required - Number of records to show per page
 *   totalRecords                             // Required - Total number of records
 *   showPageSizeSelection={ false }          // Options  - Show page size selection
 *   pageSizeSelectionOptions={ sizeOptions } // Optional - Page Size Options
 * />
 *
 * == Sorting
 *
 *  To enable column sorting, you will need to configure the Table Header component.
 * See the Table Header component documentation.
 *
 * @class Table
 * @constructor
 */
class Table extends React.Component {

  /**
   * Maintains the height of the table
   *
   * @property tableHeight
   * @type {Number}
   */
  tableHeight = 0;

  static propTypes = {
    /**
     * Emitted when table component changes e.g.
     * Pager, sorting, filter
     *
     * @property onChange
     * @type {Function}
     */
    onChange: React.PropTypes.func,

    /**
     * Show the pagination footer
     *
     * @property paginate
     * @type {Boolean}
     */
    paginate: React.PropTypes.bool,

    /**
     * Pagination
     * Current Visible Page
     *
     * @property currentPage
     * @type {String}
     */
    currentPage: React.PropTypes.string,

    /**
     * Pagination
     * Page Size of grid (number of visible records)
     *
     * @property pageSize
     * @type {String}
     */
    pageSize: React.PropTypes.string,

    /**
     * Pagination
     * Options for pageSize default - 10, 25, 50
     *
     * @property pageSizeSelectionOptions
     * @type {Object} Immutable
     */
    pageSizeSelectionOptions: React.PropTypes.object,

    /**
     * Pagination
     * Is the page size dropdown visible
     *
     * @property showPageSizeSelection
     * @type {Boolean}
     */
    showPageSizeSelection: React.PropTypes.bool,

    /**
     * Pagination
     * Total number of records in the grid
     *
     * @property totalRecords
     * @type {String}
     */
    totalRecords: React.PropTypes.string
  }

  /**
   * Lifecycle for after mounting
   * Resize the table to set the correct height on pageload
   *
   * @method componentDidMount
   * @return {Void}
   */
  componentDidMount() {
    this.resizeTable();
  }

  static childContextTypes = {
    /**
     * Defines a context object for child components of the table component.
     * https://facebook.github.io/react/docs/context.html
     *
     * @property childContextTypes
     * @type {Object}
     */
    onSort: React.PropTypes.func,
    sortedColumn: React.PropTypes.string,
    sortOrder: React.PropTypes.string
  }

  /**
   * Returns table object to child components.
   *
   * @method getChildContext
   * @return {void}
   */
  getChildContext = () => {
    return {
      onSort: this.onSort,
      sortedColumn: this.sortedColumn,
      sortOrder: this.sortOrder
    };
  }

  /**
   * Lifecycle for after a update has happened
   * If pageSize has updated to a smaller value - reset table height
   * else resize table
   *
   * @method componentDidUpdate
   * @return {Void}
   */
  componentDidUpdate(prevProps) {
    if (this.shouldResetTableHeight(prevProps)) {
      this.resetTableHeight();
    } else {
      this.resizeTable();
    }
  }

  /**
   * Reset the minHeight and tableHeight of the table
   *
   * @method resetTableHeight
   * @return {Void}
   */
  resetTableHeight() {
    this._wrapper.style.minHeight = '0';
    this.tableHeight = 0;
    setTimeout(() => {
      this.resizeTable();
    }, 0);
  }

  /**
   * Increase the minheight of the table if the new height
   * is greater than the previous
   *
   * @method resizeTable
   * @return {Void}
   */
  resizeTable() {
    if (this._table.offsetHeight > this.tableHeight) {
      this.tableHeight = this._table.offsetHeight;
      this._wrapper.style.minHeight = this.tableHeight + 'px';
    }
  }

  /**
   * Test if the table height should be reset to 0
   *
   * @method shouldResetTableHeight
   * @param prevProps - props before update
   * @return {Boolean}
   */
  shouldResetTableHeight(prevProps) {
    return prevProps.pageSize > this.pageSize;
  }

  /**
   * Get pageSize for table
   *
   * @method pageSize
   * @return {String} table page size
   */
  get pageSize() {
    return this.props.pageSize;
  }

  /**
   * Emit onChange event with options
   * needed to fetch the new data
   *
   * @method emitOnChangeCallback
   * @param {String} element changed element
   * @param {Object} options base and updated options
   * @return {Void}
   */
  emitOnChangeCallback = (element, options) => {
    this.props.onChange(element, options);
  }

  /**
   * Handles when the pager emits a onChange event
   * Passes data to emitOnChangeCallback in the correct
   * format
   *
   * @method onPagination
   * @param {String} currentPage
   * @param {String} pageSize
   * @return {Void}
   */
  onPagination = (currentPage, pageSize) => {
    let options = this.emitOptions;
    options.currentPage = currentPage;
    options.pageSize = pageSize;
    this.emitOnChangeCallback('pager', options);
  }

  /**
   * Returns the currently sorted column.
   *
   * @method sortedColumn
   * @return {String}
   */
  get sortedColumn() {
    return this.props.sortedColumn;
  }

  /**
   * Returns the current sort order.
   *
   * @method sortOrder
   * @return {String}
   */
  get sortOrder() {
    return this.props.sortOrder;
  }

  /**
   * Handles what happens on sort.
   *
   * @method onSort
   * @param {String} sortedColumn
   * @param {String} sortOrder
   */
  onSort = (sortedColumn, sortOrder) => {
    let options = this.emitOptions;
    options.sortedColumn = sortedColumn;
    options.sortOrder = sortOrder;
    this.emitOnChangeCallback('table', options);
  }

  /**
   * Base Options to be emitted by onChange
   *
   * @method emitOptions
   * @return {Object} options to emit
   */
  get emitOptions() {
    return {
      currentPage: this.props.currentPage   || '',
      pageSize: this.props.pageSize         || '',
      sortOrder: this.props.sortOrder       || '',
      sortedColumn: this.props.sortedColumn || ''
    };
  }

  /**
   * Props to pass to pager component
   *
   * @method pagerProps
   * @return {Object} props
   */
  get pagerProps() {
    return {
      currentPage: this.props.currentPage,
      onPagination: this.onPagination,
      pageSize: this.defaultPageSize,
      pageSizeSelectionOptions: this.props.pageSizeSelectionOptions,
      showPageSizeSelection: this.props.showPageSizeSelection,
      totalRecords: this.props.totalRecords
    };
  }

  /**
   * Page size for page load
   *
   * @method defaultPageSize
   * @return {Void}
   */
  get defaultPageSize() {
    if (this.props.pageSize) {
      return this.props.pageSize;
    } else if (this.props.pageSizeSelectionOptions) {
      return this.props.pageSizeSelectionOptions.first().get('id');
    }
    return '10';
  }

  /**
   * Returns the pager if paginate is true
   *
   * @method pager
   * @return {JSX} pager
   */
  get pager() {
    if (this.props.paginate) {
      return (<Pager { ...this.pagerProps } />);
    }
  }

  /**
   * Classes that apply to the parent table div
   *
   * @method mainClasses
   * @return {String}
   */
  get mainClasses() {
    return classNames(
      'ui-table',
      this.props.className
    );
  }

  /**
   * Classes that apply to the table wrapper
   *
   * @method wrapperClasses
   * @return {String}
   */
  get wrapperClasses() {
    return classNames(
      'ui-table__wrapper',
      this.props.className,
      { [`ui-table--pager`]: this.props.paginate }
    );
  }

  /**
   * Classes to apply to the table
   *
   * @method tableClasses
   * @return {String}
   */
  get tableClasses() {
    return 'ui-table__table';
  }

  /**
   * Renders the component.
   *
   * @method render
   */
  render() {
    return (

 { this._wrapper = wrapper; } } >

 { this._table = table; } } >

              { this.props.children }

        { this.pager }

    );
  }
}

export {
  Table,
  TableRow,
  TableCell,
  TableHeader
};
<|endoftext|>


<commit_before>import { readFile, writeFile } from 'fs';
import test from 'tape';
import { transform } from 'babel-core';
import bem from '..';

test('react-transform-bem', t => {
	let planned = 0;

	testFixture({
		name: 'block',
		description: 'resolves a block\'s class name'
	});

	testFixture({
		name: 'block-with-element',
		description: 'resolves a block with an element'
	});

	testFixture({
		name: 'functional',
		description: 'resolves a functional component'
	});

	t.plan(planned);

	function testFixture({ name, description, pluginOptions = {} }) {
		planned++;
		readFixture(`${name}/input.jsx`, source => {
			readFixture(`${name}/expected.js`, expected => {
				const result = transform(source, {
					presets: ['react', 'es2015'],
					plugins: [bem, pluginOptions]
				});
				t.equal(result.code, expected + '', description);
				writeFile(`test/fixtures/${name}/actual.js`, result.code);
			});
		});
	}
});

function readFixture(name, callback) {
	readFile(`test/fixtures/${name}`, (err, contents) => {
		if (err) {
			throw err;
		}
		callback(contents);
	});
}
<commit_msg>Update test descriptions<commit_after>import { readFile, writeFile } from 'fs';
import test from 'tape';
import { transform } from 'babel-core';
import bem from '..';

test('react-transform-bem', t => {
	let planned = 0;

	testFixture({
		name: 'block',
		description: 'resolves a block'
	});

	testFixture({
		name: 'block-with-element',
		description: 'resolves a block with an element'
	});

	testFixture({
		name: 'functional',
		description: 'resolves a functional component with a block'
	});

	t.plan(planned);

	function testFixture({ name, description, pluginOptions = {} }) {
		planned++;
		readFixture(`${name}/input.jsx`, source => {
			readFixture(`${name}/expected.js`, expected => {
				const result = transform(source, {
					presets: ['react', 'es2015'],
					plugins: [bem, pluginOptions]
				});
				t.equal(result.code, expected + '', description);
				writeFile(`test/fixtures/${name}/actual.js`, result.code);
			});
		});
	}
});

function readFixture(name, callback) {
	readFile(`test/fixtures/${name}`, (err, contents) => {
		if (err) {
			throw err;
		}
		callback(contents);
	});
}
<|endoftext|>


<commit_before>import React from 'react';
import TypingTestContents from './typing-test-contents';
import TypingTestInput from './typing-test-input';
import TypingTestTimer from './typing-test-timer';

class TypingTest extends Component {
    constructor() {
        this.state = { lines: [""] };
    }

    componentDidMount() {
        this.setState({ lines: this.textToLines() });
    }

    textToLines() {
        let lines = [""]
        let currentLine = 0;

        let words = this.props.text.split(" ");

        words.forEach(word => {
            // Create new line if inserting the current word would exceed
            // the max line length
            if (lines[currentLine].length + word.length > this.props.lineLength) {
                lines.push("");
                currentLine++;
            } else if (lines[currentLine].length > 0)
                lines[currentLine] += " ";
            lines[currentLine] += word;
        });
        return lines;
    }

    render() {
        return(

                <TypingTestContents text={textToLines(this.props.lineLength)} />
                <TypingTestInput onKeyPress={this.props.onKeyPress} />
                <TypingTestTimer minutes={this.props.minutes} seconds={this.props.seconds} />

);
    }
}

export default TypingTest;
<commit_msg>fix broken typing-test.js<commit_after>import React, { Component } from 'react';
import TypingTestContents from './typing-test-contents';
import TypingTestInput from './typing-test-input';
import TypingTestTimer from './typing-test-timer';

class TypingTest extends Component {
    constructor() {
        super();
        this.state = { lines: [""] };
        this.textToLines = this.textToLines.bind(this);
    }

    componentDidMount() {
        this.setState({ lines: this.textToLines() });
    }

    textToLines() {
        let lines = [""]
        let currentLine = 0;

        let words = this.props.text.split(" ");

        words.forEach(word => {
            // Create new line if inserting the current word would exceed
            // the max line length
            if (lines[currentLine].length + word.length > this.props.lineLength) {
                lines.push("");
                currentLine++;
            } else if (lines[currentLine].length > 0)
                lines[currentLine] += " ";
            lines[currentLine] += word;
        });
        return lines;
    }

    render() {
        return(

                <TypingTestContents text={this.textToLines(this.props.lineLength)} />
                <TypingTestInput onKeyPress={this.props.onKeyPress} />
                <TypingTestTimer minutes={this.props.minutes} seconds={this.props.seconds} />

);
    }
}

export default TypingTest;
<|endoftext|>


<commit_before>var test = require( 'tape' ),
	app = require('http').createServer(function(){}),
	io = require('socket.io')(app),
	forkfriend = require( 'forkfriend' ),
	innkeeper = require( './../' ),
	Room = require( 'innkeeper/lib/room' );

var sockets = [], friend = forkfriend(), keeper, room, key;

app.listen( 3333 );

test( 'reserving room', function( t ) {

	t.plan( 2 );

	io.on( 'connection', function( s ) {

		sockets.push( s );

		if( sockets.length == 3 ) {

			keeper = innkeeper();

			t.ok( keeper, 'keeper was created' );

			keeper.reserve( sockets[ 0 ] )
			.then( function( createdRoom ) {

				room = createdRoom;

				t.ok( room instanceof Room, 'received a room' );
			}, function( message ) {

				t.fail( 'failed reserving room: ' + message );
				t.end();
			});
		}
	});

	// create 3 socket.io clients (cannot run on same process otherwise it'll just be 1)
	friend.add( __dirname + '/clientTest.js' );
	friend.add( __dirname + '/clientTest.js' );
	friend.add( __dirname + '/clientTest.js' );
});

test( 'leaving room', function( t ) {

	t.plan( 1 );

	keeper.leave( sockets[ 0 ], room.id )
	.then( function( room ) {

		t.equal( room, null, 'User left room and no one is in room' );
	});
});

test( 'create another room', function( t ) {

	t.plan( 1 );

	keeper.reserve( sockets[ 0 ] )
	.then( function( createdRoom ) {

		room = createdRoom;

		t.ok( room instanceof Room, 'received a room' );
	});
});

test( 'creating a key for a room', function( t ) {

	t.plan( 1 );

	room.getKey()
	.then( function( createdKey ) {

		key = createdKey;

		t.ok( key, 'received a key to enter the room' );
	}, function() {

		t.fail( 'didnt receive key to enter room' );
	});
});

test( 'entering room with an id', function( t ) {

	t.plan( 2 );

	keeper.enter( sockets[ 1 ], room.id )
	.then( function( joinedRoom ) {

		t.ok( room === joinedRoom, 'Joined the same room' );
	}, function( message ) {

		t.fail( 'unable join existing room: ' + room.id + ' ' + message );
	});

	keeper.enter( sockets[ 0 ], room.id )
	.then( function( joinedRoom ) {

		t.fail( 'was able to join room twice with id' );
	}, function( message ) {

		t.pass( 'was not able to join room twice with id' );
	});
});

test( 'entering room with a key', function( t ) {

	t.plan( 2 );

	keeper.enterWithKey( sockets[ 2 ], key )
	.then( function( joinedRoom ) {

		t.ok( room === joinedRoom, 'Joined the same room' );
	}, function( message ) {

		t.fail( 'Failed entering with key: ' + key + ' ' + message );
	});

	keeper.enterWithKey( sockets[ 0 ], key )
	.then( function( joinedRoom ) {

		t.fail( 'was able to join room twice with key' );
	}, function( message ) {

		t.pass( 'was not able to join room twice with key' );
	});
});<commit_msg>Made tests exit<commit_after>var test = require( 'tape' ),
	app = require('http').createServer(function(){}),
	io = require('socket.io')(app),
	forkfriend = require( 'forkfriend' ),
	innkeeper = require( './../' ),
	Room = require( 'innkeeper/lib/room' );

var sockets = [], friend = forkfriend(), keeper, room, key;

app.listen( 3333 );

test( 'reserving room', function( t ) {

	t.plan( 2 );

	io.on( 'connection', function( s ) {

		sockets.push( s );

		if( sockets.length == 3 ) {

			keeper = innkeeper();

			t.ok( keeper, 'keeper was created' );

			keeper.reserve( sockets[ 0 ] )
			.then( function( createdRoom ) {

				room = createdRoom;

				t.ok( room instanceof Room, 'received a room' );
			}, function( message ) {

				t.fail( 'failed reserving room: ' + message );
				t.end();
			});
		}
	});

	// create 3 socket.io clients (cannot run on same process otherwise it'll just be 1)
	friend.add( __dirname + '/clientTest.js' );
	friend.add( __dirname + '/clientTest.js' );
	friend.add( __dirname + '/clientTest.js' );
});

test( 'leaving room', function( t ) {

	t.plan( 1 );

	keeper.leave( sockets[ 0 ], room.id )
	.then( function( room ) {

		t.equal( room, null, 'User left room and no one is in room' );
	});
});

test( 'create another room', function( t ) {

	t.plan( 1 );

	keeper.reserve( sockets[ 0 ] )
	.then( function( createdRoom ) {

		room = createdRoom;

		t.ok( room instanceof Room, 'received a room' );
	});
});

test( 'creating a key for a room', function( t ) {

	t.plan( 1 );

	room.getKey()
	.then( function( createdKey ) {

		key = createdKey;

		t.ok( key, 'received a key to enter the room' );
	}, function() {

		t.fail( 'didnt receive key to enter room' );
	});
});

test( 'entering room with an id', function( t ) {

	t.plan( 2 );

	keeper.enter( sockets[ 1 ], room.id )
	.then( function( joinedRoom ) {

		t.ok( room === joinedRoom, 'Joined the same room' );
	}, function( message ) {

		t.fail( 'unable join existing room: ' + room.id + ' ' + message );
	});

	keeper.enter( sockets[ 0 ], room.id )
	.then( function( joinedRoom ) {

		t.fail( 'was able to join room twice with id' );
	}, function( message ) {

		t.pass( 'was not able to join room twice with id' );
	});
});

test( 'entering room with a key', function( t ) {

	t.plan( 2 );

	keeper.enterWithKey( sockets[ 2 ], key )
	.then( function( joinedRoom ) {

		t.ok( room === joinedRoom, 'Joined the same room' );
	}, function( message ) {

		t.fail( 'Failed entering with key: ' + key + ' ' + message );
	});

	keeper.enterWithKey( sockets[ 0 ], key )
	.then( function( joinedRoom ) {

		t.fail( 'was able to join room twice with key' );
		endTest();
	}, function( message ) {

		t.pass( 'was not able to join room twice with key' );
		endTest();
	});
});

function endTest() {

	friend.stop();
	io.close();
}<|endoftext|>


<commit_before>var assert = require('assert'),
	proxyquire = require('proxyquire'),
	child_process = require('child_process'),

	mockContainer = {
		'./lib/cmd': 'echo "10:net_prio:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c\n9:perf_event:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c"',
		'./lib/hostname': function() { return 'a9f22af02012'; }
	},
	mockNoContainer = {
		'./lib/cmd': 'echo "10:net_prio:/\n9:perf_event:/"',
		'./lib/hostname': function() { return 'a9f22af02012'; }
	};

describe('containerized', function() {

	if (child_process.execSync) {

		it('should detect sync whether it runs inside a Docker container', function() {
			var containerized = proxyquire('../index.js', mockContainer);
			assert.equal(containerized(), true);
		});

		it('should detect sync whether it does not run inside a Docker container', function() {
			var containerized = proxyquire('../index.js', mockNoContainer);
			assert.equal(containerized(), false);
		});

	} else {

		it('should detect async whether it runs inside a Docker container', function(done) {
			var containerized = proxyquire('../index.js', {
				'./lib/cmd': 'echo "10:net_prio:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c\n9:perf_event:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c"',
				'./lib/hostname': function() { return 'a9f22af02012'; }
			});

			containerized(function(err, result) {
				assert.equal(err, null);
				assert.equal(result, true);
				done();
			});
		});

		it('should detect async whether it does not run inside a Docker container', function(done) {
			var containerized = proxyquire('../index.js', {
				'./lib/cmd': 'echo "10:net_prio:/\n9:perf_event:/"',
				'./lib/hostname': function() { return 'a9f22af02012'; }
			});

			containerized(function(err, result) {
				assert.equal(err, null);
				assert.equal(result, false);
				done();
			});
		});
	}

});<commit_msg>unify test mock objects<commit_after>var assert = require('assert'),
	proxyquire = require('proxyquire'),
	child_process = require('child_process'),

	mockContainer = {
		'./lib/cmd': 'echo "10:net_prio:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c\n9:perf_event:/docker/a9f22af020125424921a9dac4d8ab8681f7d7866da86d51e1fd97db857a51d1c"',
		'./lib/hostname': function() { return 'a9f22af02012'; }
	},
	mockNoContainer = {
		'./lib/cmd': 'echo "10:net_prio:/\n9:perf_event:/"',
		'./lib/hostname': function() { return 'a9f22af02012'; }
	};

describe('containerized', function() {

	if (child_process.execSync) {

		it('should detect sync whether it runs inside a Docker container', function() {
			var containerized = proxyquire('../index.js', mockContainer);
			assert.equal(containerized(), true);
		});

		it('should detect sync whether it does not run inside a Docker container', function() {
			var containerized = proxyquire('../index.js', mockNoContainer);
			assert.equal(containerized(), false);
		});

	} else {

		it('should detect async whether it runs inside a Docker container', function(done) {
			var containerized = proxyquire('../index.js', mockContainer);

			containerized(function(err, result) {
				assert.equal(err, null);
				assert.equal(result, true);
				done();
			});
		});

		it('should detect async whether it does not run inside a Docker container', function(done) {
			var containerized = proxyquire('../index.js', mockNoContainer);

			containerized(function(err, result) {
				assert.equal(err, null);
				assert.equal(result, false);
				done();
			});
		});
	}

});<|endoftext|>


<commit_before>// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const fresh = require('fresh');

module.exports = function (N) {

  N.validate('server_bin:core.gridfs', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      bucket: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:core.gridfs', function static_file_pre_send(env, callback) {

    if ([ 'GET', 'HEAD' ].indexOf(env.origin.req.method) === -1) {
      callback(403);
      return;
    }

    N.models.core.File.getInfo(env.params.bucket, (err, fileInfo) => {
      if (err) {
        callback(500);
        return;
      }

      if (!fileInfo) {
        callback(404);
        return;
      }

      env.data.fileInfo = fileInfo;
      callback();
    });
  });

  N.wire.on('server_bin:core.gridfs', function static_file_send(env) {
    let req = env.origin.req,
        res = env.origin.res,
        fileInfo = env.data.fileInfo;

    // Ranges are not supported yet
    // We don't have files > 5MB, cache headers are enougth
    res.removeHeader('Accept-Ranges');

    // Mark for proxies, that we can return different content (plain & gzipped),
    // res.setHeader('Vary', 'Accept-Encoding');

    res.setHeader('Date',           (new Date()).toUTCString());
    res.setHeader('Last-Modified',  fileInfo.uploadDate.toUTCString());
    res.setHeader('ETag',           fileInfo.md5);
    res.setHeader('Server',         'Windoz Sufface 3');
    // That helps browser to avoid unnecessary requests with 304 responses
    // 7 days is ok. If we decide to rebuild thumbnails, those will be
    // refreshed with reasonable delay.
    res.setHeader('Cache-Control',  'max-age=604800');

    //
    // If metadata has original name, add content-disposition header.
    // Note, that images with this header can not be opened in browser
    // by direct link - download will be started. However, images in html
    // are ok.
    //
    // On practice, we should store original names only for binary attachments.
    //

    let name = (fileInfo.metadata || {}).origName;
    if (name) {
      // Some Content-Disposition magic
      // http://stackoverflow.com/questions/93551/
      let contentDisposition;
      if (/^[\x20-\x36\x38\x40\x41\x43-\x7F]*$/.test(name)) {
        // ascii chars without *,',%
        contentDisposition = 'attachment; filename=' + name;
      } else {
        // Don't care about old browsers
        // XXX: use `ua-parser` module to add alternatives
        contentDisposition = 'attachment; filename*=UTF-8\'\'' + encodeURIComponent(name);
      }
      res.setHeader('Content-Disposition', contentDisposition);
    }

    // If we can finish with 304 reply - do it.
    if (fresh(req.headers, res._headers)) {
      env.status = 304;
      env.log_request(env);
      res.statusCode = env.status;
      res.end();
      return;
    }

    // Set content type and length headers
    res.setHeader('Content-Type', fileInfo.contentType);
    res.setHeader('Content-Length', fileInfo.length);

    //
    // Begin output
    //

    env.status = 200;
    // XXX: that will cause improper size logging on connection termination
    env.log_request(env);
    res.statusCode = env.status;

    if (req.method === 'HEAD') {
      res.end();
      return;
    }

    let stream = N.models.core.File.getStream(env.params.bucket);

    stream.on('error', err => {
      N.logger.error(`Error while streaming ${env.params.bucket} from gridfs: ${err.message}`);
      res.end();
    });

    stream.pipe(res);
  });
};
<commit_msg>Gridfs: update content-disposition header logic to RFC5987<commit_after>// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const fresh  = require('fresh');
const encode = require('mdurl/encode');

module.exports = function (N) {

  N.validate('server_bin:core.gridfs', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      bucket: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:core.gridfs', function static_file_pre_send(env, callback) {

    if ([ 'GET', 'HEAD' ].indexOf(env.origin.req.method) === -1) {
      callback(403);
      return;
    }

    N.models.core.File.getInfo(env.params.bucket, (err, fileInfo) => {
      if (err) {
        callback(500);
        return;
      }

      if (!fileInfo) {
        callback(404);
        return;
      }

      env.data.fileInfo = fileInfo;
      callback();
    });
  });

  N.wire.on('server_bin:core.gridfs', function static_file_send(env) {
    let req = env.origin.req,
        res = env.origin.res,
        fileInfo = env.data.fileInfo;

    // Ranges are not supported yet
    // We don't have files > 5MB, cache headers are enougth
    res.removeHeader('Accept-Ranges');

    // Mark for proxies, that we can return different content (plain & gzipped),
    // res.setHeader('Vary', 'Accept-Encoding');

    res.setHeader('Date',           (new Date()).toUTCString());
    res.setHeader('Last-Modified',  fileInfo.uploadDate.toUTCString());
    res.setHeader('ETag',           fileInfo.md5);
    res.setHeader('Server',         'Windoz Sufface 3');
    // That helps browser to avoid unnecessary requests with 304 responses
    // 7 days is ok. If we decide to rebuild thumbnails, those will be
    // refreshed with reasonable delay.
    res.setHeader('Cache-Control',  'max-age=604800');

    //
    // If metadata has original name, add content-disposition header.
    // Note, that images with this header can not be opened in browser
    // by direct link - download will be started. However, images in html
    // are ok.
    //
    // On practice, we should store original names only for binary attachments.
    //

    let name = (fileInfo.metadata || {}).origName;
    if (name) {
      // Content-Disposition magic (RFC5987)
      // http://stackoverflow.com/questions/93551/
      // See also http://greenbytes.de/tech/tc2231/
      let encoded   = encode(name, '!#$&+-.^_`|~', false);
      let asciified = encoded.replace(/%[0-9a-f]{2}/ig, '_');

      let contentDisposition = `attachment; filename=${asciified}`;

      if (asciified !== name) {
        contentDisposition += `; filename*=UTF-8''${encoded}`;
      }
      res.setHeader('Content-Disposition', contentDisposition);
    }

    // If we can finish with 304 reply - do it.
    if (fresh(req.headers, res._headers)) {
      env.status = 304;
      env.log_request(env);
      res.statusCode = env.status;
      res.end();
      return;
    }

    // Set content type and length headers
    res.setHeader('Content-Type', fileInfo.contentType);
    res.setHeader('Content-Length', fileInfo.length);

    //
    // Begin output
    //

    env.status = 200;
    // XXX: that will cause improper size logging on connection termination
    env.log_request(env);
    res.statusCode = env.status;

    if (req.method === 'HEAD') {
      res.end();
      return;
    }

    let stream = N.models.core.File.getStream(env.params.bucket);

    stream.on('error', err => {
      N.logger.error(`Error while streaming ${env.params.bucket} from gridfs: ${err.message}`);
      res.end();
    });

    stream.pipe(res);
  });
};
<|endoftext|>