<commit_before>'use strict'

import React, { Component } from "react";
import PropTypes from "prop-types";

import {
  View,
  TouchableOpacity,
  Animated,
  Platform
  ViewPropTypes
} from "react-native";

import S from './Style.js'

export default class FlipCard extends Component {
  static propTypes = {
    style: ViewPropTypes.style
  }

  constructor (props) {
    super(props)

    // set reversed boolean for detect other side size
    const isFlipped = (this.props.alignHeight || this.props.alignWidth) ? !props.flip : props.flip;

    this.state = {
      isFlipped: isFlipped,
      isFlipping: false,
      rotate: new Animated.Value(Number(props.flip)),
      mesured: false, // the flag to check whether it is measured or not.
      height: 0,
      width: 0,
      face: { width: 0, height: 0 },
      back: { width: 0, height: 0 }
    }
  }
  componentWillReceiveProps (nextProps) {
    if (this.state.isFlipped !== nextProps.flip) {
      this._toggleCard()
    }
  }
  _toggleCard () {
    this.setState({isFlipping: true})
    this.props.onFlipStart(this.state.isFlipped)
    this._animation(!this.state.isFlipped)
  }
  _animation (isFlipped) {
    if (!this.timer) {
      this.timer = setTimeout(() => {
        this.setState({isFlipped: !this.state.isFlipped})
        this.timer = null
      }, 120)
    }
    Animated.spring(this.state.rotate,
     {
        toValue: Number(isFlipped),
        friction: this.props.friction,
        useNativeDriver: this.props.useNativeDriver
      }
    ).start((param) => {
      this.setState({isFlipping: false})
      this.props.onFlipEnd(this.state.isFlipped)
    })
  }

  componentDidMount () {
    if (this.props.alignHeight || this.props.alignWidth) {
      // need to check the other side width or height or both
      this.measureOtherSideTimeout = setTimeout(this.measureOtherSide.bind(this), 32);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.measureOtherSideTimeout);
  }

  measureOtherSide () {
    this.setState({
      isFlipped: !this.state.isFlipped,
      mesured: true
    })
  }

  render () {
    var c = this.props.children;
    var transform = this.props.perspective ? [{ perspective: this.props.perspective }] : []
    var render_side = false

    if (this.props.flipHorizontal) {
      transform.push(
        {rotateY: this.state.rotate.interpolate({
          inputRange: [0, 1],
          outputRange: [ '0deg', '180deg' ]
        })}
      )
    }

    if (this.props.flipVertical) {
      transform.push(
        {rotateX: this.state.rotate.interpolate({
          inputRange: [0, 1],
          outputRange: [ '0deg', '180deg' ]
        })}
      )
    }

    if (this.state.isFlipped) {
      render_side = (
        <Back
          style={[ this.state.height > 0 && {height: this.state.height}, this.state.width > 0 && {width: this.state.width}]}
          flipHorizontal={this.props.flipHorizontal}
          flipVertical={this.props.flipVertical}
          perspective={this.props.perspective}
          onLayout={(event) => {
            var {x, y, width, height} = event.nativeEvent.layout
            var _update = Object.assign(this.state.back, {width: width, height: height})
            this.setState({back: _update})
            if (this.state.mesured) {
              if (this.props.alignHeight) {
                this.setState({height: Math.max(this.state.face.height, this.state.back.height)})
              }
              if (this.props.alignWidth) {
                this.setState({width: Math.max(this.state.face.width, this.state.back.width)})
              }
            }
          }}
        >
          {c[1]}
        </Back>
      )
    } else {
      render_side = (
        <Face
          style={[ this.state.height > 0 && { height: this.state.height }, this.state.width > 0 && { width: this.state.width }]}
          onLayout={(event) => {
            var {x, y, width, height} = event.nativeEvent.layout;
            var _update = Object.assign(this.state.face, {width: width, height: height})
            this.setState({face: _update})
            if (this.state.mesured) {
              if (this.props.alignHeight) {
                this.setState({height: Math.max(this.state.face.height, this.state.back.height)})
              }
              if (this.props.alignWidth) {
                this.setState({width: Math.max(this.state.face.width, this.state.back.width)})
              }
            }
          }}
        >
          {c[0]}
        </Face>
      )
    }

    // FIX: ScrollView inside Flip Card not scrollable
    // TODO: Replace below fix with by using "disabled" function (RN 0.21 exclusive)
    // REF: https://github.com/facebook/react-native/pull/5931
    //      https://github.com/facebook/react-native/issues/2103
    if (this.props.clickable) {
      let opacity = 0;
      if ( ((this.props.alignHeight || this.props.alignWidth) && this.state.mesured) ||
        !(this.props.alignHeight || this.props.alignWidth))  {
        // if you set alignXXX property, we show this side after mesured
        // Otherwise, it's showed immediately
        opacity = 1;
      }
        return (
        <TouchableOpacity
          style={{flex:1}}
          testID={this.props.testID}
          activeOpacity={1}
          onPress={() => { this._toggleCard(); }}
        >
          <Animated.View
            {...this.props}
            style={[
              S.flipCard,
              {
                transform,
                opacity,
              },
              this.props.style
            ]}
          >
            {render_side}
          </Animated.View>
        </TouchableOpacity>
      )
    } else {
      return (
        <Animated.View
          {...this.props}
          style={[S.flipCard,
          {transform: transform},
          this.props.style
          ]}
        >
          {render_side}
        </Animated.View>
      )
    }
  }
}
FlipCard.propTypes = {
  flip: PropTypes.bool,
  friction: PropTypes.number,
  perspective: PropTypes.number,
  flipHorizontal: PropTypes.bool,
  flipVertical: PropTypes.bool,
  clickable: PropTypes.bool,
  onFlipEnd: PropTypes.func,
  onFlipStart: PropTypes.func,
  alignHeight: PropTypes.bool,
  alignWidth: PropTypes.bool,
  useNativeDriver: PropTypes.bool,
  children (props, propName, componentName) {
    const prop = props[propName]
    if (React.Children.count(prop) !== 2) {
      return new Error(
        '`' + componentName + '` ' +
        'should contain exactly two children. ' +
        'The first child represents the front of the card. ' +
        'The second child represents the back of the card.'
      )
    }
  }
}

FlipCard.defaultProps = {
  flip: false,
  friction: 6,
  perspective: 0,
  flipHorizontal: false,
  flipVertical: true,
  clickable: true,
  onFlipEnd: () => {},
  onFlipStart: () => {},
  alignHeight: false,
  alignWidth: false,
  useNativeDriver: true,
}

export class Face extends Component {
  render () {
    return (
      <View
        style={[S.face, this.props.style]}
        onLayout={this.props.onLayout}
      >
        {this.props.children}
      </View>
    )
  }
}

Face.propTypes = {
  children (props, propName, componentName) {
  }
}

export class Back extends Component {
  render () {
    var transform = []
    if (this.props.flipHorizontal) {
      transform.push({scaleX: -1})
      if (Platform.OS === "android") {
        transform.push({perspective: this.props.perspective})
      }
    }
    if (this.props.flipVertical) {
      transform.push({scaleY: -1})
      if (Platform.OS === "android") {
        transform.push({perspective: this.props.perspective})
      }
    }

    return (
      <View
      style={[
        S.back,
        this.props.style,
        {transform: transform}
        ]}
        onLayout={this.props.onLayout}>
        {this.props.children}
      </View>
    )
  }
}

Back.defaultProps = {
  flipHorizontal: false,
  flipVertical: true,
  perspective: 1000,
}

Back.propTypes = {
  flipHorizontal: PropTypes.bool,
  flipVertical: PropTypes.bool,
  perspective: PropTypes.number,
  children (props, propName, componentName) {
  }
}
<commit_msg>Update FlipCard.js<commit_after>'use strict'

import React, { Component } from "react";
import PropTypes from "prop-types";

import {
  View,
  TouchableOpacity,
  Animated,
  Platform,
  ViewPropTypes
} from "react-native";

import S from './Style.js'

export default class FlipCard extends Component {
  static propTypes = {
    style: ViewPropTypes.style
  }

  constructor (props) {
    super(props)

    // set reversed boolean for detect other side size
    const isFlipped = (this.props.alignHeight || this.props.alignWidth) ? !props.flip : props.flip;

    this.state = {
      isFlipped: isFlipped,
      isFlipping: false,
      rotate: new Animated.Value(Number(props.flip)),
      mesured: false, // the flag to check whether it is measured or not.
      height: 0,
      width: 0,
      face: { width: 0, height: 0 },
      back: { width: 0, height: 0 }
    }
  }
  componentWillReceiveProps (nextProps) {
    if (this.state.isFlipped !== nextProps.flip) {
      this._toggleCard()
    }
  }
  _toggleCard () {
    this.setState({isFlipping: true})
    this.props.onFlipStart(this.state.isFlipped)
    this._animation(!this.state.isFlipped)
  }
  _animation (isFlipped) {
    if (!this.timer) {
      this.timer = setTimeout(() => {
        this.setState({isFlipped: !this.state.isFlipped})
        this.timer = null
      }, 120)
    }
    Animated.spring(this.state.rotate,
     {
        toValue: Number(isFlipped),
        friction: this.props.friction,
        useNativeDriver: this.props.useNativeDriver
      }
    ).start((param) => {
      this.setState({isFlipping: false})
      this.props.onFlipEnd(this.state.isFlipped)
    })
  }

  componentDidMount () {
    if (this.props.alignHeight || this.props.alignWidth) {
      // need to check the other side width or height or both
      this.measureOtherSideTimeout = setTimeout(this.measureOtherSide.bind(this), 32);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.measureOtherSideTimeout);
  }

  measureOtherSide () {
    this.setState({
      isFlipped: !this.state.isFlipped,
      mesured: true
    })
  }

  render () {
    var c = this.props.children;
    var transform = this.props.perspective ? [{ perspective: this.props.perspective }] : []
    var render_side = false

    if (this.props.flipHorizontal) {
      transform.push(
        {rotateY: this.state.rotate.interpolate({
          inputRange: [0, 1],
          outputRange: [ '0deg', '180deg' ]
        })}
      )
    }

    if (this.props.flipVertical) {
      transform.push(
        {rotateX: this.state.rotate.interpolate({
          inputRange: [0, 1],
          outputRange: [ '0deg', '180deg' ]
        })}
      )
    }

    if (this.state.isFlipped) {
      render_side = (
        <Back
          style={[ this.state.height > 0 && {height: this.state.height}, this.state.width > 0 && {width: this.state.width}]}
          flipHorizontal={this.props.flipHorizontal}
          flipVertical={this.props.flipVertical}
          perspective={this.props.perspective}
          onLayout={(event) => {
            var {x, y, width, height} = event.nativeEvent.layout
            var _update = Object.assign(this.state.back, {width: width, height: height})
            this.setState({back: _update})
            if (this.state.mesured) {
              if (this.props.alignHeight) {
                this.setState({height: Math.max(this.state.face.height, this.state.back.height)})
              }
              if (this.props.alignWidth) {
                this.setState({width: Math.max(this.state.face.width, this.state.back.width)})
              }
            }
          }}
        >
          {c[1]}
        </Back>
      )
    } else {
      render_side = (
        <Face
          style={[ this.state.height > 0 && { height: this.state.height }, this.state.width > 0 && { width: this.state.width }]}
          onLayout={(event) => {
            var {x, y, width, height} = event.nativeEvent.layout;
            var _update = Object.assign(this.state.face, {width: width, height: height})
            this.setState({face: _update})
            if (this.state.mesured) {
              if (this.props.alignHeight) {
                this.setState({height: Math.max(this.state.face.height, this.state.back.height)})
              }
              if (this.props.alignWidth) {
                this.setState({width: Math.max(this.state.face.width, this.state.back.width)})
              }
            }
          }}
        >
          {c[0]}
        </Face>
      )
    }

    // FIX: ScrollView inside Flip Card not scrollable
    // TODO: Replace below fix with by using "disabled" function (RN 0.21 exclusive)
    // REF: https://github.com/facebook/react-native/pull/5931
    //      https://github.com/facebook/react-native/issues/2103
    if (this.props.clickable) {
      let opacity = 0;
      if ( ((this.props.alignHeight || this.props.alignWidth) && this.state.mesured) ||
        !(this.props.alignHeight || this.props.alignWidth))  {
        // if you set alignXXX property, we show this side after mesured
        // Otherwise, it's showed immediately
        opacity = 1;
      }
        return (
        <TouchableOpacity
          style={{flex:1}}
          testID={this.props.testID}
          activeOpacity={1}
          onPress={() => { this._toggleCard(); }}
        >
          <Animated.View
            {...this.props}
            style={[
              S.flipCard,
              {
                transform,
                opacity,
              },
              this.props.style
            ]}
          >
            {render_side}
          </Animated.View>
        </TouchableOpacity>
      )
    } else {
      return (
        <Animated.View
          {...this.props}
          style={[S.flipCard,
          {transform: transform},
          this.props.style
          ]}
        >
          {render_side}
        </Animated.View>
      )
    }
  }
}
FlipCard.propTypes = {
  flip: PropTypes.bool,
  friction: PropTypes.number,
  perspective: PropTypes.number,
  flipHorizontal: PropTypes.bool,
  flipVertical: PropTypes.bool,
  clickable: PropTypes.bool,
  onFlipEnd: PropTypes.func,
  onFlipStart: PropTypes.func,
  alignHeight: PropTypes.bool,
  alignWidth: PropTypes.bool,
  useNativeDriver: PropTypes.bool,
  children (props, propName, componentName) {
    const prop = props[propName]
    if (React.Children.count(prop) !== 2) {
      return new Error(
        '`' + componentName + '` ' +
        'should contain exactly two children. ' +
        'The first child represents the front of the card. ' +
        'The second child represents the back of the card.'
      )
    }
  }
}

FlipCard.defaultProps = {
  flip: false,
  friction: 6,
  perspective: 0,
  flipHorizontal: false,
  flipVertical: true,
  clickable: true,
  onFlipEnd: () => {},
  onFlipStart: () => {},
  alignHeight: false,
  alignWidth: false,
  useNativeDriver: true,
}

export class Face extends Component {
  render () {
    return (
      <View
        style={[S.face, this.props.style]}
        onLayout={this.props.onLayout}
      >
        {this.props.children}
      </View>
    )
  }
}

Face.propTypes = {
  children (props, propName, componentName) {
  }
}

export class Back extends Component {
  render () {
    var transform = []
    if (this.props.flipHorizontal) {
      transform.push({scaleX: -1})
      if (Platform.OS === "android") {
        transform.push({perspective: this.props.perspective})
      }
    }
    if (this.props.flipVertical) {
      transform.push({scaleY: -1})
      if (Platform.OS === "android") {
        transform.push({perspective: this.props.perspective})
      }
    }

    return (
      <View
      style={[
        S.back,
        this.props.style,
        {transform: transform}
        ]}
        onLayout={this.props.onLayout}>
        {this.props.children}
      </View>
    )
  }
}

Back.defaultProps = {
  flipHorizontal: false,
  flipVertical: true,
  perspective: 1000,
}

Back.propTypes = {
  flipHorizontal: PropTypes.bool,
  flipVertical: PropTypes.bool,
  perspective: PropTypes.number,
  children (props, propName, componentName) {
  }
}
<|endoftext|>


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

var _ = require('lodash')
var colour = require('colour')

var EJSON = require('@carbon-io/ejson')
var HttpError = require('@carbon-io/http-errors').HttpError
var __ = require('@carbon-io/fibers').__(module)
var _o = require('@carbon-io/bond')._o(module)
var o = require('@carbon-io/atom').o(module)
var oo = require('@carbon-io/atom').oo(module)

var Test = require('./Test')
var HttpTestHistory = require('./HttpTestHistory')

/***************************************************************************************************
 * @class HttpTest
 */
module.exports = oo({

  /*****************************************************************************
   * _type
   */
  _type: Test,

  /*****************************************************************************
   * @constructs HttpTest
   * @description HttpTest description
   * @memberof test-tube
   * @extends test-tube.Test
   */
  _C: function() {
    /****************************************************************************
     * @property {string} baseUrl -- xxx
     */
    this.baseUrl = undefined

    /***************************************************************************
     * @property {xxx} tests -- xxx
     */
    this.tests = []
  },

  /*****************************************************************************
   * @method _init
   * @description _init description
   * @returns {undefined} -- undefined
   */
  _init: function() {
    Test.prototype._init.call(this)
  },

  /*****************************************************************************
   * @method _prerun
   * @description _prerun description
   * @param {xxx} context -- xxx
   * @returns {undefined} -- undefined
   */
  _prerun: function(context) {
    Test.prototype._prerun.call(this, context)
    context.local.__previousHttpHistory = context.httpHistory
    context.httpHistory = new HttpTestHistory()
  },

  /*****************************************************************************
   * @method _postrun
   * @description _postrun description
   * @param {xxx} context -- xxx
   * @returns {undefined} -- undefined
   */
  _postrun: function(context) {
    context.httpHistory = context.local.__previousHttpHistory
    Test.prototype._postrun.call(this, context)
  },

  /*****************************************************************************
   * @method _initTest
   * @description _initTest description
   * @param {xxx} test -- xxx
   * @returns {xxx} -- xxx
   */
  _initTest: function(test) {
    var self = this

    if (!(test instanceof Test)) {
      // Test name
      var testName = test.name
      if (!testName) {
        if (typeof(reqSpec) === 'function') {
          testName = "*DYNAMIC TEST*"
        } else {
          testName = `${test.reqSpec.method} ${test.reqSpec.url}`
        }
      }

      var _test = {
        _type: Test,
        name: testName,
        errorExpected: test.errorExpected,
        reqSpec: test.reqSpec,
        resSpec: test.resSpec,
        doTest: function(context) {
          try {
            var response = self.sync._executeHttpTest(test, context)
          } finally {
            context.httpHistory.addReq(test.name, response ? response.req : undefined)
            context.httpHistory.addRes(test.name, response)
          }
        }
      }
      if (test.setup) {
        _test.setup = test.setup
      }
      if (test.teardown) {
        _test.teardown = test.teardown
      }
      test = o(_test)
    }
    return Test.prototype._initTest.call(this, test)
  },

  /*****************************************************************************
   * @method _initTests
   * @description _initTests description
   * @returns {undefined} -- undefined
   */
  _initTests: function() {
    var self = this

    if (this.tests) {
      var result = []
      _.forEach(this.tests, function(test) {
        result.push(self._initTest(test))
      })
      self.tests = result
    }
  },

  /*****************************************************************************
   * @method _executeHttpTest
   * @description _executeHttpTest
   * @param {xxx} test -- xxx
   * @param {xxx} context -- xxx
   * @param {Function} cb -- xxx
   * @throws {Error} -- xxx
   * @returns {xxx} -- xxx
   */
  _executeHttpTest: function(test, context, cb) {
    var url = undefined
    var options = test.reqSpec.options || {}
    var reqSpec = test.reqSpec
    var resSpec = test.resSpec

    var _cb = function() {
      context.httpHistory.addResSpec(test.name, resSpec)
      context.httpHistory.addReqSpec(
        test.name, _.merge(_.cloneDeep(reqSpec), {url: url}))
      return cb.apply(null, arguments)
    }

    if (typeof(reqSpec) === 'function') {
      return this._executeHttpTest(
        this._initTest({
          name: test.name || `${reqSpec.method} ${reqSpec.url}`,
          reqSpec: reqSpec.call(test, context),
          resSpec: resSpec
        }),
        context,
        cb)
    }

    if (!reqSpec.url) {
      if (!this.baseUrl) {
        return _cb(new Error("Request spec must provide a url."))
      } else {
        url = this.baseUrl
      }
    } else { // we have reqSpec.url
      if (this.baseUrl) {
        if (_.startsWith(reqSpec.url, "http://") || _.startsWith(reqSpec.url, "https://")) {
          url = reqSpec.url
        } else {
          // concat
          url = this.baseUrl + reqSpec.url
        }
      } else { // no baseUrl
        url = reqSpec.url
      }
    }

    if (!reqSpec.method) {
      return _cb(new Error("Request spec must provide a method."))
    }

    var endpoint = _o(url, options)
    // XXX want better method to invoke here. This is private and hacky
    return endpoint._performOperation(reqSpec.method.toLowerCase(), [
      reqSpec.body,
      {
        params: reqSpec.parameters,
        headers: reqSpec.headers,
        json: reqSpec.json,
        strictSSL: reqSpec.strictSSL
      },
      function(err, response) {
        if (err && !(err instanceof HttpError)) { // If HttpError we don't freak and let the test do the testing
          return _cb(err, null)
        }

        try {
          if (typeof(resSpec) === 'function') {
            // XXX: if old tests are relying on returning true to indicate success,
            //      throw an error instructing the maintainer to instead throw an
            //      error to indicate failure and fail the test. the check here
            //      may be too weak...
            if (resSpec.toString().match(/\s+return\s+/)) {
              throw new Error('if resSpec is a function, throw an error to ' +
                              'indicate failure rather than returning true to ' +
                              'indicate success')
            }
            resSpec.call(test, response, context)
          } else {
            _.forEach(resSpec, function(valueSpec, fieldName) {
              var value = response[fieldName]
              if (typeof(valueSpec) === 'function') {
                if (valueSpec.toString().match(/\s+return\s+/)) {
                  console.warn(('WARNING: If resSpec.valueSpec is a function, throw ' +
                                'an error to indicate failure rather than ' +
                                'returning true to indicate success').yellow)
                }
                try {
                  valueSpec.call(test, value, context)
                } catch (e) {
                  e.message = "Assertion failed for field '" + fieldName +
                              "' with value '" + value + "': " + e.message
                  throw e
                }
              } else {
                assert.deepEqual(valueSpec, value, EJSON.stringify(test) +
                                                   " Response body: " +
                                                   JSON.stringify(response.body) +
                                                   " Response statusCode: " +
                                                   response.statusCode)
              }
            })
          }
        } catch (e) {
          return _cb(e)
        }

        return _cb(null, response)
      }])
  },
})
<commit_msg>Fix description for Http Tests<commit_after>var assert = require('assert')

var _ = require('lodash')
var colour = require('colour')

var EJSON = require('@carbon-io/ejson')
var HttpError = require('@carbon-io/http-errors').HttpError
var __ = require('@carbon-io/fibers').__(module)
var _o = require('@carbon-io/bond')._o(module)
var o = require('@carbon-io/atom').o(module)
var oo = require('@carbon-io/atom').oo(module)

var Test = require('./Test')
var HttpTestHistory = require('./HttpTestHistory')

/***************************************************************************************************
 * @class HttpTest
 */
module.exports = oo({

  /*****************************************************************************
   * _type
   */
  _type: Test,

  /*****************************************************************************
   * @constructs HttpTest
   * @description HttpTest description
   * @memberof test-tube
   * @extends test-tube.Test
   */
  _C: function() {
    /****************************************************************************
     * @property {string} baseUrl -- xxx
     */
    this.baseUrl = undefined

    /***************************************************************************
     * @property {xxx} tests -- xxx
     */
    this.tests = []
  },

  /*****************************************************************************
   * @method _init
   * @description _init description
   * @returns {undefined} -- undefined
   */
  _init: function() {
    Test.prototype._init.call(this)
  },

  /*****************************************************************************
   * @method _prerun
   * @description _prerun description
   * @param {xxx} context -- xxx
   * @returns {undefined} -- undefined
   */
  _prerun: function(context) {
    Test.prototype._prerun.call(this, context)
    context.local.__previousHttpHistory = context.httpHistory
    context.httpHistory = new HttpTestHistory()
  },

  /*****************************************************************************
   * @method _postrun
   * @description _postrun description
   * @param {xxx} context -- xxx
   * @returns {undefined} -- undefined
   */
  _postrun: function(context) {
    context.httpHistory = context.local.__previousHttpHistory
    Test.prototype._postrun.call(this, context)
  },

  /*****************************************************************************
   * @method _initTest
   * @description _initTest description
   * @param {xxx} test -- xxx
   * @returns {xxx} -- xxx
   */
  _initTest: function(test) {
    var self = this

    if (!(test instanceof Test)) {
      // Test name
      var testName = test.name
      if (!testName) {
        if (typeof(reqSpec) === 'function') {
          testName = "*DYNAMIC TEST*"
        } else {
          testName = `${test.reqSpec.method} ${test.reqSpec.url}`
        }
      }

      var _test = {
        _type: Test,
        name: testName,
        description: test.description,
        errorExpected: test.errorExpected,
        reqSpec: test.reqSpec,
        resSpec: test.resSpec,
        doTest: function(context) {
          try {
            var response = self.sync._executeHttpTest(test, context)
          } finally {
            context.httpHistory.addReq(test.name, response ? response.req : undefined)
            context.httpHistory.addRes(test.name, response)
          }
        }
      }
      if (test.setup) {
        _test.setup = test.setup
      }
      if (test.teardown) {
        _test.teardown = test.teardown
      }
      test = o(_test)
    }
    return Test.prototype._initTest.call(this, test)
  },

  /*****************************************************************************
   * @method _initTests
   * @description _initTests description
   * @returns {undefined} -- undefined
   */
  _initTests: function() {
    var self = this

    if (this.tests) {
      var result = []
      _.forEach(this.tests, function(test) {
        result.push(self._initTest(test))
      })
      self.tests = result
    }
  },

  /*****************************************************************************
   * @method _executeHttpTest
   * @description _executeHttpTest
   * @param {xxx} test -- xxx
   * @param {xxx} context -- xxx
   * @param {Function} cb -- xxx
   * @throws {Error} -- xxx
   * @returns {xxx} -- xxx
   */
  _executeHttpTest: function(test, context, cb) {
    var url = undefined
    var options = test.reqSpec.options || {}
    var reqSpec = test.reqSpec
    var resSpec = test.resSpec

    var _cb = function() {
      context.httpHistory.addResSpec(test.name, resSpec)
      context.httpHistory.addReqSpec(
        test.name, _.merge(_.cloneDeep(reqSpec), {url: url}))
      return cb.apply(null, arguments)
    }

    if (typeof(reqSpec) === 'function') {
      return this._executeHttpTest(
        this._initTest({
          name: test.name || `${reqSpec.method} ${reqSpec.url}`,
          reqSpec: reqSpec.call(test, context),
          resSpec: resSpec
        }),
        context,
        cb)
    }

    if (!reqSpec.url) {
      if (!this.baseUrl) {
        return _cb(new Error("Request spec must provide a url."))
      } else {
        url = this.baseUrl
      }
    } else { // we have reqSpec.url
      if (this.baseUrl) {
        if (_.startsWith(reqSpec.url, "http://") || _.startsWith(reqSpec.url, "https://")) {
          url = reqSpec.url
        } else {
          // concat
          url = this.baseUrl + reqSpec.url
        }
      } else { // no baseUrl
        url = reqSpec.url
      }
    }

    if (!reqSpec.method) {
      return _cb(new Error("Request spec must provide a method."))
    }

    var endpoint = _o(url, options)
    // XXX want better method to invoke here. This is private and hacky
    return endpoint._performOperation(reqSpec.method.toLowerCase(), [
      reqSpec.body,
      {
        params: reqSpec.parameters,
        headers: reqSpec.headers,
        json: reqSpec.json,
        strictSSL: reqSpec.strictSSL
      },
      function(err, response) {
        if (err && !(err instanceof HttpError)) { // If HttpError we don't freak and let the test do the testing
          return _cb(err, null)
        }

        try {
          if (typeof(resSpec) === 'function') {
            // XXX: if old tests are relying on returning true to indicate success,
            //      throw an error instructing the maintainer to instead throw an
            //      error to indicate failure and fail the test. the check here
            //      may be too weak...
            if (resSpec.toString().match(/\s+return\s+/)) {
              throw new Error('if resSpec is a function, throw an error to ' +
                              'indicate failure rather than returning true to ' +
                              'indicate success')
            }
            resSpec.call(test, response, context)
          } else {
            _.forEach(resSpec, function(valueSpec, fieldName) {
              var value = response[fieldName]
              if (typeof(valueSpec) === 'function') {
                if (valueSpec.toString().match(/\s+return\s+/)) {
                  console.warn(('WARNING: If resSpec.valueSpec is a function, throw ' +
                                'an error to indicate failure rather than ' +
                                'returning true to indicate success').yellow)
                }
                try {
                  valueSpec.call(test, value, context)
                } catch (e) {
                  e.message = "Assertion failed for field '" + fieldName +
                              "' with value '" + value + "': " + e.message
                  throw e
                }
              } else {
                assert.deepEqual(valueSpec, value, EJSON.stringify(test) +
                                                   " Response body: " +
                                                   JSON.stringify(response.body) +
                                                   " Response statusCode: " +
                                                   response.statusCode)
              }
            })
          }
        } catch (e) {
          return _cb(e)
        }

        return _cb(null, response)
      }])
  },
})
<|endoftext|>


<commit_before>/* global VertexData */

import gl from './constants'; // web workers don't have access to GL context, so import all GL constants
import log from 'loglevel';

// Maps GL types to JS array types
let array_types = {
    [gl.FLOAT]: Float32Array,
    [gl.BYTE]: Int8Array,
    [gl.UNSIGNED_BYTE]: Uint8Array,
    [gl.INT]: Int32Array,
    [gl.UNSIGNED_INT]: Uint32Array,
    [gl.SHORT]: Int16Array,
    [gl.UNSIGNED_SHORT]: Uint16Array
};

// An intermediary object that holds vertex data in typed arrays, according to a given vertex layout
// Used to construct a mesh/VBO for rendering
export default class VertexData {

    constructor (vertex_layout) {
        this.vertex_layout = vertex_layout;
        this.block_size = 50000;     // block size in which to allocate
        this.block_num = 1;         // initial # of allocated blocks
        this.buffer_offset = 0;      // byte offset into currently allocated buffer
        this.buffer = new ArrayBuffer(this.vertex_layout.stride * this.block_size * this.block_num);
        // this.components = [for (component of this.vertex_layout.components) [...component]]; // TODO: turn on array comprehension in traceur?
        this.components = [];
        for (var component of this.vertex_layout.components) {
            this.components.push([...component]);
        }
        this.vertex_count = 0;
        this.setBufferViews();
    }

    // (Re-)allocate typed views into the main buffer - only create the types we need for this layout
    setBufferViews () {
        this.buffer_views = {};
        for (var attrib of this.vertex_layout.attribs) {
            // Need view for this type?
            if (this.buffer_views[attrib.type] == null) {
                var array_type = array_types[attrib.type];
                this.buffer_views[attrib.type] = new array_type(this.buffer);
            }
        }

        // Update component buffer pointers
        for (var component of this.components) {
            component[1] = this.buffer_views[component[0]];
        }
    }

    // Check allocated buffer size, expand/realloc buffer if needed
    // checkBufferSize (num = 1) {
    //     if ((this.buffer_offset + (num * this.vertex_layout.stride)) > this.buffer.byteLength) {
    //         this.block_num += Math.ceil(num / this.block_size);
    checkBufferSize () {
        if ((this.buffer_offset + this.vertex_layout.stride) > this.buffer.byteLength) {
            this.block_num++;
            var new_block = new ArrayBuffer(this.vertex_layout.stride * this.block_size * this.block_num);
            var new_view = new Uint8Array(new_block);
            new_view.set(new Uint8Array(this.buffer)); // copy existing data to new buffer

            this.buffer = new_block;
            this.setBufferViews();
            log.info(`VertexData: expanded vertex block to ${this.block_size * this.block_num} vertices`);
        }
    }

    // Add a vertex, copied from a plain JS array of elements matching the order of the vertex layout.
    // Note: uses pre-calculated info about each attribute, including pointer to appropriate typed array
    // view and offset into it. This was the fastest method profiled so far for filling a mixed-type
    // vertex layout (though still slower than the previous method that only supported Float32Array attributes).
    addVertex (vertex) {
        this.checkBufferSize();
        var i=0;

        // ES6-style destructuring and iteration - cool but noticeably slower (at least w/traceur compiled code)
        // for (var [, buffer, shift, offset] of this.components) {
        //     buffer[(this.buffer_offset >> shift) + offset] = vertex[i++];

        var clen = this.components.length;
        for (var c=0; c < clen; c++) {
            var component = this.components[c];
            component[1][(this.buffer_offset >> component[2]) + component[3]] = vertex[i++];
        }

        this.buffer_offset += this.vertex_layout.stride;
        this.vertex_count++;
    }

    // Finalize vertex buffer for use in constructing a mesh
    end () {
        // Clip the allocated block to free unused memory
        if (this.buffer_offset < this.buffer.byteLength) {
            var new_block = new ArrayBuffer(this.buffer_offset);
            var new_view = new Uint8Array(new_block);
            new_view.set(new Uint8Array(this.buffer, 0, this.buffer_offset));
            this.buffer = new_block;
            this.buffer_views = null;
            this.components = null;
        }
        return this;
    }

}
<commit_msg>tuning vertex data allocation<commit_after>/* global VertexData */

import gl from './constants'; // web workers don't have access to GL context, so import all GL constants
import log from 'loglevel';

// Maps GL types to JS array types
let array_types = {
    [gl.FLOAT]: Float32Array,
    [gl.BYTE]: Int8Array,
    [gl.UNSIGNED_BYTE]: Uint8Array,
    [gl.INT]: Int32Array,
    [gl.UNSIGNED_INT]: Uint32Array,
    [gl.SHORT]: Int16Array,
    [gl.UNSIGNED_SHORT]: Uint16Array
};

// An intermediary object that holds vertex data in typed arrays, according to a given vertex layout
// Used to construct a mesh/VBO for rendering
export default class VertexData {

    constructor (vertex_layout, { prealloc } = {}) {
        this.vertex_layout = vertex_layout;
        this.buffer_size = prealloc || 5000; // # of vertices to allocate
        this.buffer_offset = 0;              // byte offset into currently allocated buffer
        this.buffer = new ArrayBuffer(this.vertex_layout.stride * this.buffer_size);
        this.components = [];
        for (var component of this.vertex_layout.components) {
            this.components.push([...component]);
        }
        this.vertex_count = 0;
        this.realloc_count = 0;
        this.setBufferViews();
    }

    // (Re-)allocate typed views into the main buffer - only create the types we need for this layout
    setBufferViews () {
        this.buffer_views = {};
        for (var attrib of this.vertex_layout.attribs) {
            // Need view for this type?
            if (this.buffer_views[attrib.type] == null) {
                var array_type = array_types[attrib.type];
                this.buffer_views[attrib.type] = new array_type(this.buffer);
            }
        }

        // Update component buffer pointers
        for (var component of this.components) {
            component[1] = this.buffer_views[component[0]];
        }
    }

    // Check allocated buffer size, expand/realloc buffer if needed
    checkBufferSize () {
        if ((this.buffer_offset + this.vertex_layout.stride) > this.buffer.byteLength) {
            this.buffer_size = Math.floor(this.buffer_size * 1.5);
            this.buffer_size -= this.buffer_size % 4;
            var new_block = new ArrayBuffer(this.vertex_layout.stride * this.buffer_size);
            var new_view = new Uint8Array(new_block);
            new_view.set(new Uint8Array(this.buffer)); // copy existing data to new buffer

            this.buffer = new_block;
            this.setBufferViews();
            this.realloc_count++;
            // log.info(`VertexData: expanded vertex block to ${this.buffer_size} vertices`);
        }
    }

    // Add a vertex, copied from a plain JS array of elements matching the order of the vertex layout.
    // Note: uses pre-calculated info about each attribute, including pointer to appropriate typed array
    // view and offset into it. This was the fastest method profiled so far for filling a mixed-type
    // vertex layout (though still slower than the previous method that only supported Float32Array attributes).
    addVertex (vertex) {
        this.checkBufferSize();
        var i=0;

        var clen = this.components.length;
        for (var c=0; c < clen; c++) {
            var component = this.components[c];
            component[1][(this.buffer_offset >> component[2]) + component[3]] = vertex[i++];
        }

        this.buffer_offset += this.vertex_layout.stride;
        this.vertex_count++;
    }

    // Finalize vertex buffer for use in constructing a mesh
    end () {
        // Clip the allocated block to free unused memory
        if (this.buffer_offset < this.buffer.byteLength) {
            var new_block = new ArrayBuffer(this.buffer_offset);
            var new_view = new Uint8Array(new_block);
            new_view.set(new Uint8Array(this.buffer, 0, this.buffer_offset));
            this.buffer = new_block;
            this.buffer_views = null;
            this.components = null;
        }
        log.debug(`VertexData: ${this.buffer_size} vertices total, realloc count ${this.realloc_count}`);
        return this;
    }

}
<|endoftext|>


<commit_before>'use strict';

/**
 * this module contains all the code related to the setup data
 */

const { FilesManager, ObjectUtils } = require('turbocommons-ts');
const console = require('./console.js');
const { execSync } = require('child_process');
const { StringUtils } = require('turbocommons-ts');

let fm = new FilesManager(require('fs'), require('os'), require('path'), process);

let isGitAvailable = false;

let isPhpAvailable = false;

/**
 * Get the turbobuilder cmd tool and project versions ready to print to console
 */
exports.getVersionNumbers = function () {

    let result = "\nturbobuilder: " + require(global.installationPaths.root + '/package.json').version;

    if (fm.isFile(global.runtimePaths.setupFile)) {

        result += "\n\n" + global.runtimePaths.projectName + ': ' + this.getCurrentSemVer();
    }

    return result;
}

/**
 * Check if the git cmd executable is available or not on the system
 */
exports.checkGitAvailable = function () {

    if(!isGitAvailable){

        try{

            execSync('git --version', {stdio : 'pipe'});

            isGitAvailable = true;

        }catch(e){

            console.error('Could not find Git cmd executable. Please install git on your system to create git changelogs');
        }
    }
}

/**
 * Check if the php cmd executable is available or not on the system
 */
exports.checkPhpAvailable = function () {

    if(!isPhpAvailable){

        try{

            execSync('php -v', {stdio : 'pipe'});

            isPhpAvailable = true;

        }catch(e){

            console.error('Could not find Php cmd executable. Please install php and make sure is available globally via cmd (add to enviroment variables).');
        }
    }
}

/**
 * Calculate the most recent project semantic version value (major.minor.patch) depending on git tags or other parameters
 */
exports.getCurrentSemVer = function () {

    this.checkGitAvailable();

    try{

        let execResult = execSync('git describe --abbrev=0 --tags', {stdio : 'pipe'});

        return StringUtils.trim(execResult.toString());

    }catch(e){

        return '0.0.0';
    }
}

/**
 * Count the number of commits since the most recent git tag
 */
exports.countCommitsSinceLatestTag = function () {

    this.checkGitAvailable();

    try{

        let execResult = execSync('git describe --abbrev=0 --tags', {stdio : 'pipe'});

        execResult = execSync('git rev-list ' + StringUtils.trim(execResult.toString()) + '.. --count', {stdio : 'pipe'});

        return StringUtils.trim(execResult.toString());

    }catch(e){

        return '0';
    }
}

/**
 * Read the xml setup file and store all the data to a global variable
 */
let loadSetupFromDisk = function () {

    if (!fm.isFile(global.runtimePaths.setupFile)) {

        console.error(global.fileNames.setup + ' setup file not found');
    }

    let templateSetupPath = global.installationPaths.mainResources + fm.dirSep() + 'project-template' + fm.dirSep() + global.fileNames.setup;

    // Load the template setup
    global.setup = JSON.parse(fm.readFile(templateSetupPath));

    // Merge the project setup into the template one
    mergeSetup(global.setup, JSON.parse(fm.readFile(global.runtimePaths.setupFile)));
};

/**
 * Merge the project custom setup with the template default one
 */
let mergeSetup = function (templateSetup, projectSetup) {

    for (let key of ObjectUtils.getKeys(templateSetup)){

        if(projectSetup.hasOwnProperty(key)){

            if(ObjectUtils.isObject(templateSetup[key])){

                mergeSetup(templateSetup[key], projectSetup[key]);

            }else{

                templateSetup[key] = projectSetup[key];
            }
        }
    }
};

/**
 * Initialize the global variables and setup structure from the project xml
 */
exports.init = function () {

    loadSetupFromDisk();
}<commit_msg>Added commit count to -v option for project version<commit_after>'use strict';

/**
 * this module contains all the code related to the setup data
 */

const { FilesManager, ObjectUtils } = require('turbocommons-ts');
const console = require('./console.js');
const { execSync } = require('child_process');
const { StringUtils } = require('turbocommons-ts');

let fm = new FilesManager(require('fs'), require('os'), require('path'), process);

let isGitAvailable = false;

let isPhpAvailable = false;

/**
 * Get the turbobuilder cmd tool and project versions ready to print to console
 */
exports.getVersionNumbers = function () {

    let result = "\nturbobuilder: " + require(global.installationPaths.root + '/package.json').version;

    if (fm.isFile(global.runtimePaths.setupFile)) {

        result += "\n\n" + global.runtimePaths.projectName + ': ' + this.getCurrentSemVer() + ' +' + this.countCommitsSinceLatestTag();
    }

    return result;
}

/**
 * Check if the git cmd executable is available or not on the system
 */
exports.checkGitAvailable = function () {

    if(!isGitAvailable){

        try{

            execSync('git --version', {stdio : 'pipe'});

            isGitAvailable = true;

        }catch(e){

            console.error('Could not find Git cmd executable. Please install git on your system to create git changelogs');
        }
    }
}

/**
 * Check if the php cmd executable is available or not on the system
 */
exports.checkPhpAvailable = function () {

    if(!isPhpAvailable){

        try{

            execSync('php -v', {stdio : 'pipe'});

            isPhpAvailable = true;

        }catch(e){

            console.error('Could not find Php cmd executable. Please install php and make sure is available globally via cmd (add to enviroment variables).');
        }
    }
}

/**
 * Calculate the most recent project semantic version value (major.minor.patch) depending on git tags or other parameters
 */
exports.getCurrentSemVer = function () {

    this.checkGitAvailable();

    try{

        let execResult = execSync('git describe --abbrev=0 --tags', {stdio : 'pipe'});

        return StringUtils.trim(execResult.toString());

    }catch(e){

        return '0.0.0';
    }
}

/**
 * Count the number of commits since the most recent git tag
 */
exports.countCommitsSinceLatestTag = function () {

    this.checkGitAvailable();

    try{

        let execResult = execSync('git describe --abbrev=0 --tags', {stdio : 'pipe'});

        execResult = execSync('git rev-list ' + StringUtils.trim(execResult.toString()) + '.. --count', {stdio : 'pipe'});

        return StringUtils.trim(execResult.toString());

    }catch(e){

        return '0';
    }
}

/**
 * Read the xml setup file and store all the data to a global variable
 */
let loadSetupFromDisk = function () {

    if (!fm.isFile(global.runtimePaths.setupFile)) {

        console.error(global.fileNames.setup + ' setup file not found');
    }

    let templateSetupPath = global.installationPaths.mainResources + fm.dirSep() + 'project-template' + fm.dirSep() + global.fileNames.setup;

    // Load the template setup
    global.setup = JSON.parse(fm.readFile(templateSetupPath));

    // Merge the project setup into the template one
    mergeSetup(global.setup, JSON.parse(fm.readFile(global.runtimePaths.setupFile)));
};

/**
 * Merge the project custom setup with the template default one
 */
let mergeSetup = function (templateSetup, projectSetup) {

    for (let key of ObjectUtils.getKeys(templateSetup)){

        if(projectSetup.hasOwnProperty(key)){

            if(ObjectUtils.isObject(templateSetup[key])){

                mergeSetup(templateSetup[key], projectSetup[key]);

            }else{

                templateSetup[key] = projectSetup[key];
            }
        }
    }
};

/**
 * Initialize the global variables and setup structure from the project xml
 */
exports.init = function () {

    loadSetupFromDisk();
}<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const os = require('os');
const path = require('path');
const removeFolder = require('rimraf');
const childProcess = require('child_process');
const BrowserFetcher = require('./BrowserFetcher');
const {Connection} = require('./Connection');
const {Browser} = require('./Browser');
const readline = require('readline');
const fs = require('fs');
const {helper, assert, debugError} = require('./helper');
const ChromiumRevision = require(path.join(helper.projectRoot(), 'package.json')).puppeteer.chromium_revision;

const mkdtempAsync = helper.promisify(fs.mkdtemp);
const removeFolderAsync = helper.promisify(removeFolder);

const CHROME_PROFILE_PATH = path.join(os.tmpdir(), 'puppeteer_dev_profile-');

const DEFAULT_ARGS = [
  '--disable-background-networking',
  '--disable-background-timer-throttling',
  '--disable-client-side-phishing-detection',
  '--disable-default-apps',
  '--disable-dev-shm-usage',
  '--disable-extensions',
  '--disable-hang-monitor',
  '--disable-popup-blocking',
  '--disable-prompt-on-repost',
  '--disable-sync',
  '--disable-translate',
  '--metrics-recording-only',
  '--no-first-run',
  '--safebrowsing-disable-auto-update',
];

const AUTOMATION_ARGS = [
  '--enable-automation',
  '--password-store=basic',
  '--use-mock-keychain',
];

class Launcher {
  /**
   * @param {!LaunchOptions=} options
   * @return {!Promise<!Browser>}
   */
  static async launch(options) {
    options = Object.assign({}, options || {});
    assert(!options.ignoreDefaultArgs || !options.appMode, '`appMode` flag cannot be used together with `ignoreDefaultArgs`');
    let temporaryUserDataDir = null;
    const chromeArguments = [];
    if (!options.ignoreDefaultArgs)
      chromeArguments.push(...DEFAULT_ARGS);
    if (options.appMode) {
      options.headless = false;
      options.pipe = true;
    } else if (!options.ignoreDefaultArgs) {
      chromeArguments.push(...AUTOMATION_ARGS);
    }

    if (!options.ignoreDefaultArgs || !chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
      chromeArguments.push(options.pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');

    if (!options.args || !options.args.some(arg => arg.startsWith('--user-data-dir'))) {
      if (!options.userDataDir)
        temporaryUserDataDir = await mkdtempAsync(CHROME_PROFILE_PATH);

      chromeArguments.push(`--user-data-dir=${options.userDataDir || temporaryUserDataDir}`);
    }
    if (options.devtools === true) {
      chromeArguments.push('--auto-open-devtools-for-tabs');
      options.headless = false;
    }
    if (typeof options.headless !== 'boolean' || options.headless) {
      chromeArguments.push(
          '--headless',
          '--disable-gpu',
          '--hide-scrollbars',
          '--mute-audio'
      );
    }
    if (!options.ignoreDefaultArgs && Array.isArray(options.args) && options.args.every(arg => arg.startsWith('-')))
      chromeArguments.push('about:blank');

    let chromeExecutable = options.executablePath;
    if (typeof chromeExecutable !== 'string') {
      const browserFetcher = new BrowserFetcher();
      const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
      assert(revisionInfo.local, `Chromium revision is not downloaded. Run "npm install" or "yarn install"`);
      chromeExecutable = revisionInfo.executablePath;
    }
    if (Array.isArray(options.args))
      chromeArguments.push(...options.args);

    const usePipe = chromeArguments.includes('--remote-debugging-pipe');
    const stdio = ['pipe', 'pipe', 'pipe'];
    if (usePipe)
      stdio.push('pipe', 'pipe');
    const chromeProcess = childProcess.spawn(
        chromeExecutable,
        chromeArguments,
        {
          // On non-windows platforms, `detached: false` makes child process a leader of a new
          // process group, making it possible to kill child process tree with `.kill(-pid)` command.
          // @see https://nodejs.org/api/child_process.html#child_process_options_detached
          detached: process.platform !== 'win32',
          env: options.env || process.env,
          stdio
        }
    );

    if (options.dumpio) {
      chromeProcess.stderr.pipe(process.stderr);
      chromeProcess.stdout.pipe(process.stdout);
    }

    let chromeClosed = false;
    const waitForChromeToClose = new Promise((fulfill, reject) => {
      chromeProcess.once('close', () => {
        chromeClosed = true;
        // Cleanup as processes exit.
        if (temporaryUserDataDir) {
          removeFolderAsync(temporaryUserDataDir)
              .then(() => fulfill())
              .catch(err => console.error(err));
        } else {
          fulfill();
        }
      });
    });

    const listeners = [ helper.addEventListener(process, 'exit', killChrome) ];
    if (options.handleSIGINT !== false)
      listeners.push(helper.addEventListener(process, 'SIGINT', () => { killChrome(); process.exit(130); }));
    if (options.handleSIGTERM !== false)
      listeners.push(helper.addEventListener(process, 'SIGTERM', gracefullyCloseChrome));
    if (options.handleSIGHUP !== false)
      listeners.push(helper.addEventListener(process, 'SIGHUP', gracefullyCloseChrome));
    /** @type {?Connection} */
    let connection = null;
    try {
      const connectionDelay = options.slowMo || 0;
      if (!usePipe) {
        const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000;
        const browserWSEndpoint = await waitForWSEndpoint(chromeProcess, timeout);
        connection = await Connection.createForWebSocket(browserWSEndpoint, connectionDelay);
      } else {
        connection = Connection.createForPipe(/** @type {!NodeJS.WritableStream} */(chromeProcess.stdio[3]), /** @type {!NodeJS.ReadableStream} */ (chromeProcess.stdio[4]), connectionDelay);
      }
      const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors;
      const setDefaultViewport = !options.appMode;
      return Browser.create(connection, [], ignoreHTTPSErrors, setDefaultViewport, chromeProcess, gracefullyCloseChrome);
    } catch (e) {
      killChrome();
      throw e;
    }

    /**
     * @return {Promise}
     */
    function gracefullyCloseChrome() {
      helper.removeEventListeners(listeners);
      if (temporaryUserDataDir) {
        killChrome();
      } else if (connection) {
        // Attempt to close chrome gracefully
        connection.send('Browser.close').catch(error => {
          debugError(error);
          killChrome();
        });
      }
      return waitForChromeToClose;
    }

    // This method has to be sync to be used as 'exit' event handler.
    function killChrome() {
      helper.removeEventListeners(listeners);
      if (chromeProcess.pid && !chromeProcess.killed && !chromeClosed) {
        // Force kill chrome.
        try {
          if (process.platform === 'win32')
            childProcess.execSync(`taskkill /pid ${chromeProcess.pid} /T /F`);
          else
            process.kill(-chromeProcess.pid, 'SIGKILL');
        } catch (e) {
          // the process might have already stopped
        }
      }
      // Attempt to remove temporary profile directory to avoid littering.
      try {
        removeFolder.sync(temporaryUserDataDir);
      } catch (e) { }
    }
  }

  /**
   * @return {!Array<string>}
   */
  static defaultArgs() {
    return DEFAULT_ARGS.concat(AUTOMATION_ARGS);
  }

  /**
   * @return {string}
   */
  static executablePath() {
    const browserFetcher = new BrowserFetcher();
    const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
    return revisionInfo.executablePath;
  }

  /**
   * @param {!Object=} options
   * @return {!Promise<!Browser>}
   */
  static async connect(options = {}) {
    const connectionDelay = options.slowMo || 0;
    const connection = await Connection.createForWebSocket(options.browserWSEndpoint, connectionDelay);
    const {browserContextIds} = await connection.send('Target.getBrowserContexts');
    const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors;
    return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, true /* setDefaultViewport */, null, () => connection.send('Browser.close').catch(debugError));
  }
}

/**
 * @param {!Puppeteer.ChildProcess} chromeProcess
 * @param {number} timeout
 * @return {!Promise<string>}
 */
function waitForWSEndpoint(chromeProcess, timeout) {
  return new Promise((resolve, reject) => {
    const rl = readline.createInterface({ input: chromeProcess.stderr });
    let stderr = '';
    const listeners = [
      helper.addEventListener(rl, 'line', onLine),
      helper.addEventListener(rl, 'close', () => onClose()),
      helper.addEventListener(chromeProcess, 'exit', () => onClose()),
      helper.addEventListener(chromeProcess, 'error', error => onClose(error))
    ];
    const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;

    /**
     * @param {!Error=} error
     */
    function onClose(error) {
      cleanup();
      reject(new Error([
        'Failed to launch chrome!' + (error ? ' ' + error.message : ''),
        stderr,
        '',
        'TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md',
        '',
      ].join('\n')));
    }

    function onTimeout() {
      cleanup();
      reject(new Error(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${ChromiumRevision}`));
    }

    /**
     * @param {string} line
     */
    function onLine(line) {
      stderr += line + '\n';
      const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
      if (!match)
        return;
      cleanup();
      resolve(match[1]);
    }

    function cleanup() {
      if (timeoutId)
        clearTimeout(timeoutId);
      helper.removeEventListeners(listeners);
    }
  });
}

/**
 * @typedef {Object} LaunchOptions
 * @property {boolean=} ignoreHTTPSErrors
 * @property {boolean=} headless
 * @property {string=} executablePath
 * @property {number=} slowMo
 * @property {!Array<string>=} args
 * @property {boolean=} ignoreDefaultArgs
 * @property {boolean=} handleSIGINT
 * @property {boolean=} handleSIGTERM
 * @property {boolean=} handleSIGHUP
 * @property {number=} timeout
 * @property {boolean=} dumpio
 * @property {string=} userDataDir
 * @property {!Object<string, string | undefined>=} env
 * @property {boolean=} devtools
 * @property {boolean=} pipe
 * @property {boolean=} appMode
 */

module.exports = Launcher;
<commit_msg>feat(launcher): disable crash reporting by default (#2652)<commit_after>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const os = require('os');
const path = require('path');
const removeFolder = require('rimraf');
const childProcess = require('child_process');
const BrowserFetcher = require('./BrowserFetcher');
const {Connection} = require('./Connection');
const {Browser} = require('./Browser');
const readline = require('readline');
const fs = require('fs');
const {helper, assert, debugError} = require('./helper');
const ChromiumRevision = require(path.join(helper.projectRoot(), 'package.json')).puppeteer.chromium_revision;

const mkdtempAsync = helper.promisify(fs.mkdtemp);
const removeFolderAsync = helper.promisify(removeFolder);

const CHROME_PROFILE_PATH = path.join(os.tmpdir(), 'puppeteer_dev_profile-');

const DEFAULT_ARGS = [
  '--disable-background-networking',
  '--disable-background-timer-throttling',
  '--disable-breakpad',
  '--disable-client-side-phishing-detection',
  '--disable-default-apps',
  '--disable-dev-shm-usage',
  '--disable-extensions',
  '--disable-hang-monitor',
  '--disable-popup-blocking',
  '--disable-prompt-on-repost',
  '--disable-sync',
  '--disable-translate',
  '--metrics-recording-only',
  '--no-first-run',
  '--safebrowsing-disable-auto-update',
];

const AUTOMATION_ARGS = [
  '--enable-automation',
  '--password-store=basic',
  '--use-mock-keychain',
];

class Launcher {
  /**
   * @param {!LaunchOptions=} options
   * @return {!Promise<!Browser>}
   */
  static async launch(options) {
    options = Object.assign({}, options || {});
    assert(!options.ignoreDefaultArgs || !options.appMode, '`appMode` flag cannot be used together with `ignoreDefaultArgs`');
    let temporaryUserDataDir = null;
    const chromeArguments = [];
    if (!options.ignoreDefaultArgs)
      chromeArguments.push(...DEFAULT_ARGS);
    if (options.appMode) {
      options.headless = false;
      options.pipe = true;
    } else if (!options.ignoreDefaultArgs) {
      chromeArguments.push(...AUTOMATION_ARGS);
    }

    if (!options.ignoreDefaultArgs || !chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
      chromeArguments.push(options.pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');

    if (!options.args || !options.args.some(arg => arg.startsWith('--user-data-dir'))) {
      if (!options.userDataDir)
        temporaryUserDataDir = await mkdtempAsync(CHROME_PROFILE_PATH);

      chromeArguments.push(`--user-data-dir=${options.userDataDir || temporaryUserDataDir}`);
    }
    if (options.devtools === true) {
      chromeArguments.push('--auto-open-devtools-for-tabs');
      options.headless = false;
    }
    if (typeof options.headless !== 'boolean' || options.headless) {
      chromeArguments.push(
          '--headless',
          '--disable-gpu',
          '--hide-scrollbars',
          '--mute-audio'
      );
    }
    if (!options.ignoreDefaultArgs && Array.isArray(options.args) && options.args.every(arg => arg.startsWith('-')))
      chromeArguments.push('about:blank');

    let chromeExecutable = options.executablePath;
    if (typeof chromeExecutable !== 'string') {
      const browserFetcher = new BrowserFetcher();
      const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
      assert(revisionInfo.local, `Chromium revision is not downloaded. Run "npm install" or "yarn install"`);
      chromeExecutable = revisionInfo.executablePath;
    }
    if (Array.isArray(options.args))
      chromeArguments.push(...options.args);

    const usePipe = chromeArguments.includes('--remote-debugging-pipe');
    const stdio = ['pipe', 'pipe', 'pipe'];
    if (usePipe)
      stdio.push('pipe', 'pipe');
    const chromeProcess = childProcess.spawn(
        chromeExecutable,
        chromeArguments,
        {
          // On non-windows platforms, `detached: false` makes child process a leader of a new
          // process group, making it possible to kill child process tree with `.kill(-pid)` command.
          // @see https://nodejs.org/api/child_process.html#child_process_options_detached
          detached: process.platform !== 'win32',
          env: options.env || process.env,
          stdio
        }
    );

    if (options.dumpio) {
      chromeProcess.stderr.pipe(process.stderr);
      chromeProcess.stdout.pipe(process.stdout);
    }

    let chromeClosed = false;
    const waitForChromeToClose = new Promise((fulfill, reject) => {
      chromeProcess.once('close', () => {
        chromeClosed = true;
        // Cleanup as processes exit.
        if (temporaryUserDataDir) {
          removeFolderAsync(temporaryUserDataDir)
              .then(() => fulfill())
              .catch(err => console.error(err));
        } else {
          fulfill();
        }
      });
    });

    const listeners = [ helper.addEventListener(process, 'exit', killChrome) ];
    if (options.handleSIGINT !== false)
      listeners.push(helper.addEventListener(process, 'SIGINT', () => { killChrome(); process.exit(130); }));
    if (options.handleSIGTERM !== false)
      listeners.push(helper.addEventListener(process, 'SIGTERM', gracefullyCloseChrome));
    if (options.handleSIGHUP !== false)
      listeners.push(helper.addEventListener(process, 'SIGHUP', gracefullyCloseChrome));
    /** @type {?Connection} */
    let connection = null;
    try {
      const connectionDelay = options.slowMo || 0;
      if (!usePipe) {
        const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000;
        const browserWSEndpoint = await waitForWSEndpoint(chromeProcess, timeout);
        connection = await Connection.createForWebSocket(browserWSEndpoint, connectionDelay);
      } else {
        connection = Connection.createForPipe(/** @type {!NodeJS.WritableStream} */(chromeProcess.stdio[3]), /** @type {!NodeJS.ReadableStream} */ (chromeProcess.stdio[4]), connectionDelay);
      }
      const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors;
      const setDefaultViewport = !options.appMode;
      return Browser.create(connection, [], ignoreHTTPSErrors, setDefaultViewport, chromeProcess, gracefullyCloseChrome);
    } catch (e) {
      killChrome();
      throw e;
    }

    /**
     * @return {Promise}
     */
    function gracefullyCloseChrome() {
      helper.removeEventListeners(listeners);
      if (temporaryUserDataDir) {
        killChrome();
      } else if (connection) {
        // Attempt to close chrome gracefully
        connection.send('Browser.close').catch(error => {
          debugError(error);
          killChrome();
        });
      }
      return waitForChromeToClose;
    }

    // This method has to be sync to be used as 'exit' event handler.
    function killChrome() {
      helper.removeEventListeners(listeners);
      if (chromeProcess.pid && !chromeProcess.killed && !chromeClosed) {
        // Force kill chrome.
        try {
          if (process.platform === 'win32')
            childProcess.execSync(`taskkill /pid ${chromeProcess.pid} /T /F`);
          else
            process.kill(-chromeProcess.pid, 'SIGKILL');
        } catch (e) {
          // the process might have already stopped
        }
      }
      // Attempt to remove temporary profile directory to avoid littering.
      try {
        removeFolder.sync(temporaryUserDataDir);
      } catch (e) { }
    }
  }

  /**
   * @return {!Array<string>}
   */
  static defaultArgs() {
    return DEFAULT_ARGS.concat(AUTOMATION_ARGS);
  }

  /**
   * @return {string}
   */
  static executablePath() {
    const browserFetcher = new BrowserFetcher();
    const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
    return revisionInfo.executablePath;
  }

  /**
   * @param {!Object=} options
   * @return {!Promise<!Browser>}
   */
  static async connect(options = {}) {
    const connectionDelay = options.slowMo || 0;
    const connection = await Connection.createForWebSocket(options.browserWSEndpoint, connectionDelay);
    const {browserContextIds} = await connection.send('Target.getBrowserContexts');
    const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors;
    return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, true /* setDefaultViewport */, null, () => connection.send('Browser.close').catch(debugError));
  }
}

/**
 * @param {!Puppeteer.ChildProcess} chromeProcess
 * @param {number} timeout
 * @return {!Promise<string>}
 */
function waitForWSEndpoint(chromeProcess, timeout) {
  return new Promise((resolve, reject) => {
    const rl = readline.createInterface({ input: chromeProcess.stderr });
    let stderr = '';
    const listeners = [
      helper.addEventListener(rl, 'line', onLine),
      helper.addEventListener(rl, 'close', () => onClose()),
      helper.addEventListener(chromeProcess, 'exit', () => onClose()),
      helper.addEventListener(chromeProcess, 'error', error => onClose(error))
    ];
    const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;

    /**
     * @param {!Error=} error
     */
    function onClose(error) {
      cleanup();
      reject(new Error([
        'Failed to launch chrome!' + (error ? ' ' + error.message : ''),
        stderr,
        '',
        'TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md',
        '',
      ].join('\n')));
    }

    function onTimeout() {
      cleanup();
      reject(new Error(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${ChromiumRevision}`));
    }

    /**
     * @param {string} line
     */
    function onLine(line) {
      stderr += line + '\n';
      const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
      if (!match)
        return;
      cleanup();
      resolve(match[1]);
    }

    function cleanup() {
      if (timeoutId)
        clearTimeout(timeoutId);
      helper.removeEventListeners(listeners);
    }
  });
}

/**
 * @typedef {Object} LaunchOptions
 * @property {boolean=} ignoreHTTPSErrors
 * @property {boolean=} headless
 * @property {string=} executablePath
 * @property {number=} slowMo
 * @property {!Array<string>=} args
 * @property {boolean=} ignoreDefaultArgs
 * @property {boolean=} handleSIGINT
 * @property {boolean=} handleSIGTERM
 * @property {boolean=} handleSIGHUP
 * @property {number=} timeout
 * @property {boolean=} dumpio
 * @property {string=} userDataDir
 * @property {!Object<string, string | undefined>=} env
 * @property {boolean=} devtools
 * @property {boolean=} pipe
 * @property {boolean=} appMode
 */

module.exports = Launcher;
<|endoftext|>


<commit_before>'use strict';

var React = require('react');
var assign = require('react/lib/Object.assign');
var Scroller = require('scroller');
var Group = require('./Group');
var clamp = require('./clamp');

var ListView = React.createClass({

  propTypes: {
    style: React.PropTypes.object,
    numberOfItemsGetter: React.PropTypes.func.isRequired,
    itemHeightGetter: React.PropTypes.func.isRequired,
    itemGetter: React.PropTypes.func.isRequired,
    snapping: React.PropTypes.bool,
    scrollingDeceleration: React.PropTypes.number,
    scrollingPenetrationAcceleration: React.PropTypes.number,
    onScroll: React.PropTypes.func
  },

  getDefaultProps: function () {
    return {
      style: { left: 0, top: 0, width: 0, height: 0 },
      snapping: false,
      scrollingDeceleration: 0.95,
      scrollingPenetrationAcceleration: 0.08
    };
  },

  getInitialState: function () {
    return {
      scrollTop: 0
    };
  },

  componentDidMount: function () {
    this.createScroller();
    this.updateScrollingDimensions();
  },

  render: function () {
    var items = this.getVisibleItemIndexes().map(this.renderItem);
    return (
      React.createElement(Group, {
        style: this.props.style,
        onTouchStart: this.handleTouchStart,
        onTouchMove: this.handleTouchMove,
        onTouchEnd: this.handleTouchEnd,
        onTouchCancel: this.handleTouchEnd},
        items
      )
    );
  },

  renderItem: function (itemIndex) {
    var item = this.props.itemGetter(itemIndex, this.state.scrollTop);
    var itemHeight = this.props.itemHeightGetter();
    var listViewTop = this.props.style.top || 0;
    var listViewLeft = this.props.style.left || 0;

    var style = {
      top: listViewTop,
      left: listViewLeft,
      width: this.props.style.width,
      height: itemHeight,
      translateY: (itemIndex * itemHeight) - this.state.scrollTop + listViewTop,
      zIndex: itemIndex
    };

    return (
      React.createElement(Group, {style: style, key: itemIndex},
        item
      )
    );
  },

  // Events
  // ======

  handleTouchStart: function (e) {
    if (this.scroller) {
      this.scroller.doTouchStart(e.touches, e.timeStamp);
    }
  },

  handleTouchMove: function (e) {
    if (this.scroller) {
      e.preventDefault();
      this.scroller.doTouchMove(e.touches, e.timeStamp, e.scale);
    }
  },

  handleTouchEnd: function (e) {
    if (this.scroller) {
      this.scroller.doTouchEnd(e.timeStamp);
      if (this.props.snapping) {
        this.updateScrollingDeceleration();
      }
    }
  },

  handleScroll: function (left, top) {
    this.setState({ scrollTop: top });
    if (this.props.onScroll) {
      this.props.onScroll(top);
    }
  },

  // Scrolling
  // =========

  createScroller: function () {
    var options = {
      scrollingX: false,
      scrollingY: true,
      decelerationRate: this.props.scrollingDeceleration,
      penetrationAcceleration: this.props.scrollingPenetrationAcceleration,
    };
    this.scroller = new Scroller(this.handleScroll, options);
  },

  updateScrollingDimensions: function () {
    var width = this.props.style.width;
    var height = this.props.style.height;
    var scrollWidth = width;
    var scrollHeight = this.props.numberOfItemsGetter() * this.props.itemHeightGetter();
    this.scroller.setDimensions(width, height, scrollWidth, scrollHeight);
  },

  getVisibleItemIndexes: function () {
    var itemIndexes = [];
    var itemHeight = this.props.itemHeightGetter();
    var itemCount = this.props.numberOfItemsGetter();
    var scrollTop = this.state.scrollTop;
    var itemScrollTop = 0;

    for (var index=0; index < itemCount; index++) {
      itemScrollTop = (index * itemHeight) - scrollTop;

      // Item is completely off-screen bottom
      if (itemScrollTop >= this.props.style.height) {
        continue;
      }

      // Item is completely off-screen top
      if (itemScrollTop <= -this.props.style.height) {
        continue;
      }

      // Part of item is on-screen.
      itemIndexes.push(index);
    }

    return itemIndexes;
  },

  updateScrollingDeceleration: function () {
    var currVelocity = this.scroller.__decelerationVelocityY;
    var currScrollTop = this.state.scrollTop;
    var targetScrollTop = 0;
    var estimatedEndScrollTop = currScrollTop;

    while (Math.abs(currVelocity).toFixed(6) > 0) {
      estimatedEndScrollTop += currVelocity;
      currVelocity *= this.props.scrollingDeceleration;
    }

    // Find the page whose estimated end scrollTop is closest to 0.
    var closestZeroDelta = Infinity;
    var pageHeight = this.props.itemHeightGetter();
    var pageCount = this.props.numberOfItemsGetter();
    var pageScrollTop;

    for (var pageIndex=0, len=pageCount; pageIndex < len; pageIndex++) {
      pageScrollTop = (pageHeight * pageIndex) - estimatedEndScrollTop;
      if (Math.abs(pageScrollTop) < closestZeroDelta) {
        closestZeroDelta = Math.abs(pageScrollTop);
        targetScrollTop = pageHeight * pageIndex;
      }
    }

    this.scroller.__minDecelerationScrollTop = targetScrollTop;
    this.scroller.__maxDecelerationScrollTop = targetScrollTop;
  }

});

module.exports = ListView;
<commit_msg>missing translateX<commit_after>'use strict';

var React = require('react');
var assign = require('react/lib/Object.assign');
var Scroller = require('scroller');
var Group = require('./Group');
var clamp = require('./clamp');

var ListView = React.createClass({

  propTypes: {
    style: React.PropTypes.object,
    numberOfItemsGetter: React.PropTypes.func.isRequired,
    itemHeightGetter: React.PropTypes.func.isRequired,
    itemGetter: React.PropTypes.func.isRequired,
    snapping: React.PropTypes.bool,
    scrollingDeceleration: React.PropTypes.number,
    scrollingPenetrationAcceleration: React.PropTypes.number,
    onScroll: React.PropTypes.func
  },

  getDefaultProps: function () {
    return {
      style: { left: 0, top: 0, width: 0, height: 0 },
      snapping: false,
      scrollingDeceleration: 0.95,
      scrollingPenetrationAcceleration: 0.08
    };
  },

  getInitialState: function () {
    return {
      scrollTop: 0
    };
  },

  componentDidMount: function () {
    this.createScroller();
    this.updateScrollingDimensions();
  },

  render: function () {
    var items = this.getVisibleItemIndexes().map(this.renderItem);
    return (
      React.createElement(Group, {
        style: this.props.style,
        onTouchStart: this.handleTouchStart,
        onTouchMove: this.handleTouchMove,
        onTouchEnd: this.handleTouchEnd,
        onTouchCancel: this.handleTouchEnd},
        items
      )
    );
  },

  renderItem: function (itemIndex) {
    var item = this.props.itemGetter(itemIndex, this.state.scrollTop);
    var itemHeight = this.props.itemHeightGetter();
    var listViewTop = this.props.style.top || 0;
    var listViewLeft = this.props.style.left || 0;

    var style = {
      top: listViewTop,
      left: listViewLeft,
      width: this.props.style.width,
      height: itemHeight,
      translateX: listViewLeft,
      translateY: (itemIndex * itemHeight) - this.state.scrollTop + listViewTop,
      zIndex: itemIndex
    };

    return (
      React.createElement(Group, {style: style, key: itemIndex},
        item
      )
    );
  },

  // Events
  // ======

  handleTouchStart: function (e) {
    if (this.scroller) {
      this.scroller.doTouchStart(e.touches, e.timeStamp);
    }
  },

  handleTouchMove: function (e) {
    if (this.scroller) {
      e.preventDefault();
      this.scroller.doTouchMove(e.touches, e.timeStamp, e.scale);
    }
  },

  handleTouchEnd: function (e) {
    if (this.scroller) {
      this.scroller.doTouchEnd(e.timeStamp);
      if (this.props.snapping) {
        this.updateScrollingDeceleration();
      }
    }
  },

  handleScroll: function (left, top) {
    this.setState({ scrollTop: top });
    if (this.props.onScroll) {
      this.props.onScroll(top);
    }
  },

  // Scrolling
  // =========

  createScroller: function () {
    var options = {
      scrollingX: false,
      scrollingY: true,
      decelerationRate: this.props.scrollingDeceleration,
      penetrationAcceleration: this.props.scrollingPenetrationAcceleration,
    };
    this.scroller = new Scroller(this.handleScroll, options);
  },

  updateScrollingDimensions: function () {
    var width = this.props.style.width;
    var height = this.props.style.height;
    var scrollWidth = width;
    var scrollHeight = this.props.numberOfItemsGetter() * this.props.itemHeightGetter();
    this.scroller.setDimensions(width, height, scrollWidth, scrollHeight);
  },

  getVisibleItemIndexes: function () {
    var itemIndexes = [];
    var itemHeight = this.props.itemHeightGetter();
    var itemCount = this.props.numberOfItemsGetter();
    var scrollTop = this.state.scrollTop;
    var itemScrollTop = 0;

    for (var index=0; index < itemCount; index++) {
      itemScrollTop = (index * itemHeight) - scrollTop;

      // Item is completely off-screen bottom
      if (itemScrollTop >= this.props.style.height) {
        continue;
      }

      // Item is completely off-screen top
      if (itemScrollTop <= -this.props.style.height) {
        continue;
      }

      // Part of item is on-screen.
      itemIndexes.push(index);
    }

    return itemIndexes;
  },

  updateScrollingDeceleration: function () {
    var currVelocity = this.scroller.__decelerationVelocityY;
    var currScrollTop = this.state.scrollTop;
    var targetScrollTop = 0;
    var estimatedEndScrollTop = currScrollTop;

    while (Math.abs(currVelocity).toFixed(6) > 0) {
      estimatedEndScrollTop += currVelocity;
      currVelocity *= this.props.scrollingDeceleration;
    }

    // Find the page whose estimated end scrollTop is closest to 0.
    var closestZeroDelta = Infinity;
    var pageHeight = this.props.itemHeightGetter();
    var pageCount = this.props.numberOfItemsGetter();
    var pageScrollTop;

    for (var pageIndex=0, len=pageCount; pageIndex < len; pageIndex++) {
      pageScrollTop = (pageHeight * pageIndex) - estimatedEndScrollTop;
      if (Math.abs(pageScrollTop) < closestZeroDelta) {
        closestZeroDelta = Math.abs(pageScrollTop);
        targetScrollTop = pageHeight * pageIndex;
      }
    }

    this.scroller.__minDecelerationScrollTop = targetScrollTop;
    this.scroller.__maxDecelerationScrollTop = targetScrollTop;
  }

});

module.exports = ListView;
<|endoftext|>


<commit_before>'use strict';

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
const url = require('url');
const logger = require('../logger');

/**
 * @param {string} userAgent user agent
 * @param {object} puppeteer puppeteer options
 * @param {object} tbs extra options for TBS request parameter
 */
class GoogleScraper {
  constructor({
    userAgent = 'Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0',
    scrollDelay = 500,
    puppeteer = {},
    tbs = {},
  } = {}) {
    this.userAgent = userAgent;
    this.scrollDelay = scrollDelay;
    this.puppeteerOptions = puppeteer;
    this.tbs = this._parseRequestParameters(tbs);
  }

  _parseRequestParameters(tbs) {
    if (tbs === undefined) {
      return '';
    }

    const options = Object.keys(tbs)
      .filter(key => tbs[key])
      .map(key => `${key}:${tbs[key]}`)
      .join(',');
    return encodeURIComponent(options);
  }

  async scrape(searchQuery, limit = 100) {
    if (searchQuery === undefined || searchQuery === '') {
      throw new Error('Invalid search query provided');
    }
    const query = `https://www.google.com/search?q=${searchQuery}&source=lnms&tbm=isch&sa=X&tbs=${this.tbs}`;

    logger.info(`Start Google search for "${searchQuery}"`);
    const browser = await puppeteer.launch({
      ...this.puppeteerOptions,
    });
    const page = await browser.newPage();
    await page.setBypassCSP(true);
    await page.goto(query, {
      waitUntil: 'networkidle0',
    });
    await page.setViewport({ width: 1920, height: 1080 });
    await page.setUserAgent(this.userAgent);

    let results = [];
    let previousCount = -1;
    while (results.length < limit) {
      await this._scrollToEnd(page);
      await this._clickAllImages(page);

      const html = await page.content();
      const links = this._parseLinksFromHTML(html);
      previousCount = results.length;
      results = links.slice(0, limit);
      if (previousCount === results.length) {
        logger.debug('End of the page is reached');
        break;
      }

      logger.debug(`Got ${results.length} results so far`);
    }

    await browser.close();
    return results;
  }

  /**
   * Scroll to the end of the page.
   * @param {page} Puppeteer page to scroll
   */
  async _scrollToEnd(page) {
    logger.debug('Scrolling to the end of the page');

    const isScrollable = await this._isScrollable(page);
    if (!isScrollable) {
      logger.debug('No results on this page');
      return;
    }

    const buttonIsVisible = await this._isButtonVisible(page);
    await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
    logger.debug(`Scrolled to bottom of the page`);

    if (buttonIsVisible) {
      await page.click("#islmp input[type='button']");
      logger.debug('Clicked on show more results');
    }

    await page.waitFor(this.scrollDelay);
    await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
  }

  _isScrollable(page) {
    return page.evaluate(() => {
      return document.querySelector("#islmp input[type='button']") !== null;
    });
  }

  _isButtonVisible(page) {
    return page.evaluate(() => {
      function isVisible(e) {
        return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
      }
      return isVisible(document.querySelector("#islmp input[type='button']"));
    });
  }

  _getInfiniteScrollStatus(page) {
    return page.evaluate(() => {
      let status = document.querySelector('#islmp div[data-endedmessage] > div:last-child')
        .innerText;
      return status;
    });
  }

  async _clickAllImages(page) {
    logger.debug('Scrolling to the end of the page');
    return page.evaluate(() => {
      let elements = document.querySelectorAll('#islrg img');

      function rightClick(element) {
        return new Promise(resolve => {
          let event = new MouseEvent('mousedown', {
            bubbles: true,
            cancelable: false,
            view: window,
            button: 2,
            buttons: 2,
            clientX: element.getBoundingClientRect().x,
            clientY: element.getBoundingClientRect().y,
          });
          element.dispatchEvent(event);
          resolve();
        });
      }

      async function rightClickAll(elements) {
        for (const element of elements) {
          await rightClick(element);
        }
      }
      rightClickAll(elements);
    });
  }

  _parseLinksFromHTML(html) {
    let links = [];

    let $ = cheerio.load(html);

    $("#islrg a[href^='/imgres']").each(function(i, elem) {
      let description = $(this)
        .next()
        .find('div > div:first-child')
        .text();

      let link = $(this).attr('href');

      let parsedLink = url.parse(link, { parseQueryString: true });
      let imageurl = parsedLink.query.imgurl;
      let source = parsedLink.query.imgrefurl;

      links.push({ url: imageurl, source, description });
    });

    return links;
  }
}

module.exports = GoogleScraper;
<commit_msg>fix JS injection delay<commit_after>'use strict';

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
const url = require('url');
const logger = require('../logger');

/**
 * @param {string} userAgent user agent
 * @param {object} puppeteer puppeteer options
 * @param {object} tbs extra options for TBS request parameter
 */
class GoogleScraper {
  constructor({
    userAgent = 'Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0',
    scrollDelay = 500,
    puppeteer = {},
    tbs = {},
  } = {}) {
    this.userAgent = userAgent;
    this.scrollDelay = scrollDelay;
    this.puppeteerOptions = puppeteer;
    this.tbs = this._parseRequestParameters(tbs);
  }

  _parseRequestParameters(tbs) {
    if (tbs === undefined) {
      return '';
    }

    const options = Object.keys(tbs)
      .filter(key => tbs[key])
      .map(key => `${key}:${tbs[key]}`)
      .join(',');
    return encodeURIComponent(options);
  }

  async scrape(searchQuery, limit = 100) {
    if (searchQuery === undefined || searchQuery === '') {
      throw new Error('Invalid search query provided');
    }
    const query = `https://www.google.com/search?q=${searchQuery}&source=lnms&tbm=isch&sa=X&tbs=${this.tbs}`;

    logger.info(`Start Google search for "${searchQuery}"`);
    const browser = await puppeteer.launch({
      ...this.puppeteerOptions,
    });
    const page = await browser.newPage();
    await page.setBypassCSP(true);
    await page.goto(query, {
      waitUntil: 'networkidle0',
    });
    await page.setViewport({ width: 1920, height: 1080 });
    await page.setUserAgent(this.userAgent);

    let results = [];
    let previousCount = -1;
    while (results.length < limit) {
      await this._scrollToEnd(page);
      await this._clickAllImages(page);
      await page.waitFor(200);  // Delay needed to inject the JS elements

      const html = await page.content();
      const links = this._parseLinksFromHTML(html);
      previousCount = results.length;
      results = links.slice(0, limit);
      if (previousCount === results.length) {
        logger.debug('End of the page is reached');
        break;
      }

      logger.debug(`Got ${results.length} results so far`);
    }

    await browser.close();
    return results;
  }

  /**
   * Scroll to the end of the page.
   * @param {page} Puppeteer page to scroll
   */
  async _scrollToEnd(page) {
    logger.debug('Scrolling to the end of the page');

    const isScrollable = await this._isScrollable(page);
    if (!isScrollable) {
      logger.debug('No results on this page');
      return;
    }

    const buttonIsVisible = await this._isButtonVisible(page);
    await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
    logger.debug(`Scrolled to bottom of the page`);

    if (buttonIsVisible) {
      await page.click("#islmp input[type='button']");
      logger.debug('Clicked on show more results');
    }

    await page.waitFor(this.scrollDelay);
    await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
  }

  _isScrollable(page) {
    return page.evaluate(() => {
      return document.querySelector("#islmp input[type='button']") !== null;
    });
  }

  _isButtonVisible(page) {
    return page.evaluate(() => {
      function isVisible(e) {
        return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
      }
      return isVisible(document.querySelector("#islmp input[type='button']"));
    });
  }

  _getInfiniteScrollStatus(page) {
    return page.evaluate(() => {
      let status = document.querySelector('#islmp div[data-endedmessage] > div:last-child')
        .innerText;
      return status;
    });
  }

  async _clickAllImages(page) {
    logger.debug('Scrolling to the end of the page');
    return page.evaluate(() => {
      let elements = document.querySelectorAll('#islrg img');

      function rightClick(element) {
        return new Promise(resolve => {
          let event = new MouseEvent('mousedown', {
            bubbles: true,
            cancelable: false,
            view: window,
            button: 2,
            buttons: 2,
            clientX: element.getBoundingClientRect().x,
            clientY: element.getBoundingClientRect().y,
          });
          element.dispatchEvent(event);
          resolve();
        });
      }

      async function rightClickAll(elements) {
        for (const element of elements) {
          await rightClick(element);
        }
      }
      rightClickAll(elements);
    });
  }

  _parseLinksFromHTML(html) {
    let links = [];

    let $ = cheerio.load(html);

    $("#islrg a[href^='/imgres']").each(function(i, elem) {
      let description = $(this)
        .next()
        .find('div > div:first-child')
        .text();

      let link = $(this).attr('href');

      let parsedLink = url.parse(link, { parseQueryString: true });
      let imageurl = parsedLink.query.imgurl;
      let source = parsedLink.query.imgrefurl;

      links.push({ url: imageurl, source, description });
    });

    return links;
  }
}

module.exports = GoogleScraper;
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var QuestionSchema = new Schema({
	'Relation' : {
	 	type: Schema.Types.ObjectId,
	 	ref: 'Relation'
	},
	'Phrase' : String,
	'Anwser' : Array,
	'Utenti' : Array
});

module.exports = mongoose.model('Question', QuestionSchema);
<commit_msg>Test new branch<commit_after>var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var QuestionSchema = new Schema({
	'Relation' : {
	 	type: Schema.Types.ObjectId,
	 	ref: 'Relation'
	},
	'Phrase' : String,
	'Anwser' : Array,
	'Utenti' : Array
});

module.exports = mongoose.model('Question', QuestionSchema);
<|endoftext|>


<commit_before>var Context = require('./Context');

function Material(program, uniforms) {
  this.gl = Context.currentContext;
  this.program = program;
  this.uniforms = uniforms || {};
}

Material.prototype.use = function () {
  this.program.use();
  var numTextures = 0;
  for (var name in this.program.uniforms) {
    if (this.program.uniforms[name].type == this.gl.SAMPLER_2D || this.program.uniforms[name].type == this.gl.SAMPLER_CUBE) {
      this.gl.activeTexture(this.gl.TEXTURE0 + numTextures);
      //bind only loaded / initialized textures
      if (this.uniforms[name] && this.uniforms[name].width > 0 && this.uniforms[name].height > 0) {
        this.gl.bindTexture(this.uniforms[name].target, this.uniforms[name].handle);
        this.program.uniforms[name](numTextures);
      }
      numTextures++;
    }
    else if (this.uniforms[name]) {
      this.program.uniforms[name](this.uniforms[name]);
    }
  }
};

module.exports = Material;<commit_msg>Removed check for empty uniforms<commit_after>var Context = require('./Context');

function Material(program, uniforms) {
  this.gl = Context.currentContext;
  this.program = program;
  this.uniforms = uniforms || {};
}

Material.prototype.use = function () {
  this.program.use();
  var numTextures = 0;
  for (var name in this.program.uniforms) {
    if (this.program.uniforms[name].type == this.gl.SAMPLER_2D || this.program.uniforms[name].type == this.gl.SAMPLER_CUBE) {
      this.gl.activeTexture(this.gl.TEXTURE0 + numTextures);
      //bind only loaded / initialized textures
      if (this.uniforms[name] && this.uniforms[name].width > 0 && this.uniforms[name].height > 0) {
        this.gl.bindTexture(this.uniforms[name].target, this.uniforms[name].handle);
        this.program.uniforms[name](numTextures);
      }
      numTextures++;
    }
    else {
      this.program.uniforms[name](this.uniforms[name]);
    }
  }
};

module.exports = Material;
<|endoftext|>