<commit_before>import React from 'react';
import Relay from 'react-relay';

import Favorite from '../../icons/Favorite';
import Emojify from '../../shared/Emojify';

import permissions from '../../../lib/permissions';

import PusherStore from '../../../stores/PusherStore';

import PipelineFavoriteMutation from '../../../mutations/PipelineFavorite';

import Status from './status';
import Metrics from './Metrics';
import Graph from './graph';

class Pipeline extends React.Component {
  static propTypes = {
    pipeline: React.PropTypes.shape({
      id: React.PropTypes.string.isRequired,
      name: React.PropTypes.string.isRequired,
      slug: React.PropTypes.string.isRequired,
      description: React.PropTypes.string,
      defaultBranch: React.PropTypes.string.isRequired,
      favorite: React.PropTypes.bool.isRequired,
      url: React.PropTypes.string.isRequired,
      permissions: React.PropTypes.shape({
        pipelineFavorite: React.PropTypes.shape({
          allowed: React.PropTypes.bool.isRequired
        }).isRequired
      })
    }).isRequired,
    relay: React.PropTypes.object.isRequired
  };

  state = {
    showingMenu: false
  };

  componentDidMount() {
    PusherStore.on("websocket:event", this.handlePusherWebsocketEvent);
  }

  componentWillUnmount() {
    PusherStore.off("websocket:event", this.handlePusherWebsocketEvent);
  }

  render() {
    return (

          <Status pipeline={this.props.pipeline} />

{this.props.pipeline.name}

            {this.renderDescription()}

        {this.renderGraph()}

          <Metrics pipeline={this.props.pipeline} />

        {this.renderActions()}

    );
  }

  renderDescription() {
    if (this.props.pipeline.description) {
      return (

          <Emojify className="h5 regular" text={this.props.pipeline.description} />

      );
    }
  }

  renderGraph() {
    // Toggle between showing the graph, or showing a placeholder until the
    // data is finally loaded in.
    let graph;
    if (this.props.relay.variables.includeGraphData) {
      graph = (

{this.props.pipeline.defaultBranch}

          <Graph pipeline={this.props.pipeline} />

      );
    } else {
      graph = (

      );
    }

    return (

        {graph}

    );
  }

  renderActions() {
    // Make sure we're allowed to favorite this pipeline
    const favoriteButton = permissions(this.props.pipeline.permissions).check(
      {
        allowed: "pipelineFavorite",
        render: () => {
          return (
            <button className="btn p0 dark-gray line-height-1" onClick={this.handleFavoriteClick}>
              <Favorite favorite={this.props.pipeline.favorite} />
            </button>
          );
        }
      }
    );

    return (

        {favoriteButton}

    );
  }

  handlePusherWebsocketEvent = (payload) => {
    if (payload.event === "project:updated" && payload.graphql.id === this.props.pipeline.id) {
      this.props.relay.forceFetch();
    }
  };

  handleMenuToggle = (visible) => {
    this.setState({ showingMenu: visible });
  };

  handleFavoriteClick = () => {
    const mutation = new PipelineFavoriteMutation({
      pipeline: this.props.pipeline,
      favorite: !this.props.pipeline.favorite
    });

    Relay.Store.commitUpdate(mutation, {
      onFailure: this.handlePipelineFavoriteMutationFailure
    });
  }

  handlePipelineFavoriteMutationFailure = (transaction) => {
    alert(transaction.getError());
  }
}

export default Relay.createContainer(Pipeline, {
  initialVariables: {
    includeGraphData: false
  },

  fragments: {
    pipeline: (variables) => Relay.QL`
      fragment on Pipeline {
        ${PipelineFavoriteMutation.getFragment('pipeline')}
        ${Status.getFragment('pipeline')}
        ${Metrics.getFragment('pipeline')}
        ${Graph.getFragment('pipeline').if(variables.includeGraphData)}
        id
        name
        slug
        description
        defaultBranch
        url
        favorite
        permissions {
          pipelineFavorite {
            allowed
          }
        }
      }
    `
  }
});
<commit_msg>Fix eslint problems<commit_after>import React from 'react';
import Relay from 'react-relay';

import Favorite from '../../icons/Favorite';
import Emojify from '../../shared/Emojify';

import permissions from '../../../lib/permissions';

import PusherStore from '../../../stores/PusherStore';

import PipelineFavoriteMutation from '../../../mutations/PipelineFavorite';

import Status from './status';
import Metrics from './Metrics';
import Graph from './graph';

class Pipeline extends React.Component {
  static propTypes = {
    pipeline: React.PropTypes.shape({
      id: React.PropTypes.string.isRequired,
      name: React.PropTypes.string.isRequired,
      slug: React.PropTypes.string.isRequired,
      description: React.PropTypes.string,
      defaultBranch: React.PropTypes.string.isRequired,
      favorite: React.PropTypes.bool.isRequired,
      url: React.PropTypes.string.isRequired,
      permissions: React.PropTypes.shape({
        pipelineFavorite: React.PropTypes.shape({
          allowed: React.PropTypes.bool.isRequired
        }).isRequired
      })
    }).isRequired,
    relay: React.PropTypes.object.isRequired
  };

  state = {
    showingMenu: false
  };

  componentDidMount() {
    PusherStore.on("websocket:event", this.handlePusherWebsocketEvent);
  }

  componentWillUnmount() {
    PusherStore.off("websocket:event", this.handlePusherWebsocketEvent);
  }

  render() {
    return (

          <Status pipeline={this.props.pipeline} />

{this.props.pipeline.name}

            {this.renderDescription()}

        {this.renderGraph()}

          <Metrics pipeline={this.props.pipeline} />

        {this.renderActions()}

    );
  }

  renderDescription() {
    if (this.props.pipeline.description) {
      return (

          <Emojify className="h5 regular" text={this.props.pipeline.description} />

      );
    }
  }

  renderGraph() {
    // Toggle between showing the graph, or showing a placeholder until the
    // data is finally loaded in.
    let graph;
    if (this.props.relay.variables.includeGraphData) {
      graph = (

{this.props.pipeline.defaultBranch}

          <Graph pipeline={this.props.pipeline} />

      );
    } else {
      graph = (

      );
    }

    return (

        {graph}

    );
  }

  renderActions() {
    // Make sure we're allowed to favorite this pipeline
    const favoriteButton = permissions(this.props.pipeline.permissions).check(
      {
        allowed: "pipelineFavorite",
        render: () => {
          return (
            <button className="btn p0 dark-gray line-height-1" onClick={this.handleFavoriteClick}>
              <Favorite favorite={this.props.pipeline.favorite} />
            </button>
          );
        }
      }
    );

    return (

        {favoriteButton}

    );
  }

  handlePusherWebsocketEvent = (payload) => {
    if (payload.event === "project:updated" && payload.graphql.id === this.props.pipeline.id) {
      this.props.relay.forceFetch();
    }
  };

  handleMenuToggle = (visible) => {
    this.setState({ showingMenu: visible });
  };

  handleFavoriteClick = () => {
    const mutation = new PipelineFavoriteMutation({
      pipeline: this.props.pipeline,
      favorite: !this.props.pipeline.favorite
    });

    Relay.Store.commitUpdate(mutation, {
      onFailure: this.handlePipelineFavoriteMutationFailure
    });
  }

  handlePipelineFavoriteMutationFailure = (transaction) => {
    alert(transaction.getError());
  }
}

export default Relay.createContainer(Pipeline, {
  initialVariables: {
    includeGraphData: false
  },

  fragments: {
    pipeline: (variables) => Relay.QL`
      fragment on Pipeline {
        ${PipelineFavoriteMutation.getFragment('pipeline')}
        ${Status.getFragment('pipeline')}
        ${Metrics.getFragment('pipeline')}
        ${Graph.getFragment('pipeline').if(variables.includeGraphData)}
        id
        name
        slug
        description
        defaultBranch
        url
        favorite
        permissions {
          pipelineFavorite {
            allowed
          }
        }
      }
    `
  }
});
<|endoftext|>


<commit_before>/**
 * Created by hebao on 2017/8/24.
 */
'use strict';
import React, {Component} from 'react';
import {
  View,
  Text,
  StyleSheet,
  ListView,
  Dimensions,
  TouchableHighlight,
  ActivityIndicator
}  from 'react-native';
import RefresherFlatList from './PTRScrollList';
import HBStyle from '../../styles/standard';
import Util from '../../utility/util';

class FooterInfinite extends Component {
  static defaultProps = {
    gestureStatus: 1
  };

  constructor(props) {
    super(props);
  }

  render() {
    let {gestureStatus} = this.props, _refreshFont = '';
    switch (gestureStatus) {
      case 1:
        _refreshFont = '上拉加载更多...';
        break;
      case 3:
        _refreshFont = '松开加载更多...';
        break;
      case 5:
        _refreshFont = '加载中...';
        break;
      default:
        _refreshFont = '上拉加载更多...';
    }
    return (
      <View style={Styles.footerInfinite}>
        {gestureStatus === 5 ?
          <ActivityIndicator
            size={'small'}
            animating={true}
            color={'#75c5fe'}
            style={{marginRight: 10}}/> : null}
        <Text style={Styles.refreshFont}>{_refreshFont}</Text>
      </View>
    );
  }
}

class HeaderRefresh extends Component {
  static defaultProps = {
    gestureStatus: 2
  };

  constructor(props) {
    super(props);
  }

  render() {
    let {gestureStatus} = this.props, _refreshFont = '';
    switch (gestureStatus) {
      case 2:
        _refreshFont = '下拉刷新...';
        break;
      case 3:
        _refreshFont = '释放刷新...';
        break;
      case 4:
        _refreshFont = '正在刷新...';
        break;
      default:
        _refreshFont = '下拉刷新...';
    }
    return (
      <View style={Styles.headerRefresh}>
        {gestureStatus === 4 ?
          <ActivityIndicator
            size={'small'}
            animating={true}
            color={'#75c5fe'}
            style={{marginRight: 10}}/> : null}
        <Text style={Styles.refreshFont}>{_refreshFont}</Text>
      </View>
    );
  }
}

const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
export default class Example extends Component {
  _timer = -1;
  data = [];

  constructor(props) {
    super(props)
    this.getData()
    this.state = {
      dataSource: ds.cloneWithRows(this.data),
    }
  }

  getData(init) {
    let total = 4;
    if (init) {
      this.data = [];
      total = Math.ceil(Math.random() * 17)
    }
    for (let i = 0; i < total; i++) {
      this.data.push('row' + Math.ceil(Math.random() * total));
    }
  }

  renderRow = (rowData, sectionID, rowID) => {
    return (
      <View
        style={Styles.flatListItem}>
        <Text style={Styles.font_3}>{rowData + ':' + rowID}</Text>
      </View>
    )
  }

  ItemSeparatorComponent = ({highlighted}) => {
    return <View style={[Styles.separator, {backgroundColor: highlighted ? '#FDFE3C' : '#feafea'}]}/>
  }

  renderItem = ({item, index, separators}) => {
    return (
      <TouchableHighlight
        onPress={() => console.log(item + ' pressed!!!')}
        activeOpacity={1}
        underlayColor={'#e8e8e8'}
        onShowUnderlay={separators.highlight}
        onHideUnderlay={separators.unhighlight}
        style={Styles.flatListItem}>
        <Text style={Styles.font_3}>{item + ':' + index}</Text>
      </TouchableHighlight>
    )
  }

  render() {
    return (
      <View style={Styles.wrap}>
        <View style={{height: 40, width: Dimensions.get('window').width, backgroundColor: '#d5c639'}}/>
        <RefresherFlatList
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          showsVerticalScrollIndicator={false}

          scrollComponent={'ListView'}

          enableHeaderRefresh={true}
          renderHeaderRefresh={(gestureStatus) => <HeaderRefresh gestureStatus={gestureStatus}/>}
          setHeaderHeight={100}
          onHeaderRefreshing={() => {
            clearTimeout(this._timer)
            this._timer = setTimeout(() => {
              this.getData(true)
              this.setState({
                dataSource: ds.cloneWithRows(this.data)
              }, () => {
                RefresherFlatList.headerRefreshDone()
              })
            }, 1000)
          }}

          enableFooterInfinite={true}
          renderFooterInfinite={(gestureStatus) => <FooterInfinite gestureStatus={gestureStatus}/>}
          setFooterHeight={60}
          onFooterInfiniting={() => {
            clearTimeout(this._timer)
            this._timer = setTimeout(() => {
              this.getData()
              this.setState({
                dataSource: ds.cloneWithRows(this.data)
              }, () => {
                RefresherFlatList.footerInfiniteDone()
              })
            }, 15000)
          }}
        />
      </View>
    )
    // return (
    //   <View style={Styles.wrap}>
    //     <View style={{height: 40, width: Dimensions.get('window').width, backgroundColor: '#142124'}}/>
    //     <RefresherFlatList
    //       data={this.data}
    //       ItemSeparatorComponent={this.ItemSeparatorComponent}
    //       renderItem={this.renderItem}
    //       keyExtractor={(item, index) => ('flatPTR_' + index)}
    //       showsVerticalScrollIndicator={false}
    //       contentContainerStyle={{backgroundColor: '#ffffff'}}
    //
    //       scrollComponent={'FlatList'}
    //
    //       enableHeaderRefresh={true}
    //       renderHeaderRefresh={(gestureStatus) => <HeaderRefresh gestureStatus={gestureStatus}/>}
    //       onHeaderRefreshing={() => {
    //         clearTimeout(this._timer)
    //         this._timer = setTimeout(() => {
    //           this.getData(true)
    //           this.setState({
    //             dataSource: ds.cloneWithRows(this.data)
    //           }, () => {
    //             RefresherFlatList.headerRefreshDone()
    //           })
    //         }, 5000)
    //       }}
    //
    //       enableFooterInfinite={true}
    //       renderFooterInfinite={(gestureStatus) => <FooterInfinite gestureStatus={gestureStatus}/>}
    //       onFooterInfiniting={() => {
    //         clearTimeout(this._timer)
    //         this._timer = setTimeout(() => {
    //           this.getData()
    //           this.setState({
    //             dataSource: ds.cloneWithRows(this.data)
    //           }, () => {
    //             RefresherFlatList.footerInfiniteDone()
    //           })
    //         }, 1000)
    //       }}
    //     />
    //   </View>
    // )
  }
}

const Styles = StyleSheet.create({
  wrap: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'center',
    backgroundColor: HBStyle.color.gray_bg
  },
  commonRowSC: {
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  commonColumnSS: {
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
  },
  protocolWrap: {
    width: Util.size.screen.width,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    backgroundColor: HBStyle.color.wwhite,
    marginTop: 15,
    borderTopWidth: 0.5,
    borderBottomWidth: 0.5,
    borderColor: HBStyle.color.common_gray_line
  },
  protocol: {
    width: Util.size.screen.width,
    height: 44,
    paddingHorizontal: 16,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  font_1: {
    fontSize: 16,
    color: HBStyle.color.text_black_w
  },
  font_2: {
    fontSize: 14,
    color: HBStyle.color.text_gray_w_main,
  },
  font_3: {
    fontSize: 15,
    color: HBStyle.color.text_black_w
  },
  font_4: {
    fontSize: 14,
    color: HBStyle.color.text_red_w
  },
  font_5: {
    fontSize: 12,
    color: HBStyle.color.text_white_w,
    backgroundColor: 'transparent'
  },
  font_creditor: {
    fontSize: 16,
    color: HBStyle.color.text_black_w,
    marginTop: 15,
  },
  font_creditorDetail: {
    fontSize: 15,
    color: HBStyle.color.text_gray_w_main,
    marginTop: 20,
  },
  retractFont: {
    fontSize: 15,
    color: HBStyle.color.text_white_w,
  },
  divideLine: {
    width: Util.size.screen.width,
    height: 0.5,
    backgroundColor: HBStyle.color.common_gray_line,
  },
  iconNext: {
    width: 7,
    height: 13
  },
  creditorWrap: {
    width: Util.size.screen.width,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    backgroundColor: HBStyle.color.wwhite,
    marginTop: 15,
    paddingLeft: 16,
    paddingRight: 14,
    borderTopWidth: 0.5,
    borderTopColor: HBStyle.color.common_gray_line
  },
  listViewWrap: {
    flex: 1,
    width: Util.size.screen.width,
    backgroundColor: HBStyle.color.gray_bg
  },
  listItem: {
    width: Util.size.screen.width,
    backgroundColor: HBStyle.color.wwhite,
    paddingVertical: 16,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
  },
  itemWrap: {
    width: Util.size.screen.width,
    paddingHorizontal: 14,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  itemProgressWrap: {
    width: 126,
    height: 20,
    overflow: 'hidden',
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 7 * Util.size.screen.pixelRatio,
  },
  endLoadMore: {
    width: Util.size.screen.width,
    height: 40,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: HBStyle.color.gray_bg
  },
  loadMoreFont: {
    fontSize: 12,
    color: HBStyle.color.text_gray_w_main
  },
  headerRefresh: {
    width: Dimensions.get('window').width,
    height: 100,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center'
  },
  footerInfinite: {
    width: Dimensions.get('window').width,
    height: 60,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center'
  },
  refreshFont: {
    fontSize: 16,
    color: '#b84f35'
  },
  separator: {
    width: '100%',
    height: 0.5,
    marginLeft: 10
  },
  flatListItem: {
    width: Dimensions.get('window').width,
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: '#feafea'
  }
});<commit_msg>git rename test<commit_after>/**
 * Created by hebao on 2017/8/24.
 */
'use strict';
import React, {Component} from 'react';
import {
  View,
  Text,
  StyleSheet,
  ListView,
  Dimensions,
  TouchableHighlight,
  ActivityIndicator
}  from 'react-native';
import RefresherFlatList from './PTRScrollList';
import HBStyle from '../../styles/standard';
import Util from '../../utility/util';

class FooterInfinite extends Component {
  static defaultProps = {
    gestureStatus: 1
  };

  constructor(props) {
    super(props);
  }

  render() {
    let {gestureStatus} = this.props, _refreshFont = '';
    switch (gestureStatus) {
      case 1:
        _refreshFont = '上拉加载更多...';
        break;
      case 3:
        _refreshFont = '松开加载更多...';
        break;
      case 5:
        _refreshFont = '加载中...';
        break;
      default:
        _refreshFont = '上拉加载更多...';
    }
    return (
      <View style={Styles.footerInfinite}>
        {gestureStatus === 5 ?
          <ActivityIndicator
            size={'small'}
            animating={true}
            color={'#75c5fe'}
            style={{marginRight: 10}}/> : null}
        <Text style={Styles.refreshFont}>{_refreshFont}</Text>
      </View>
    );
  }
}

class HeaderRefresh extends Component {
  static defaultProps = {
    gestureStatus: 2
  };

  constructor(props) {
    super(props);
  }

  render() {
    let {gestureStatus} = this.props, _refreshFont = '';
    switch (gestureStatus) {
      case 2:
        _refreshFont = '下拉刷新...';
        break;
      case 3:
        _refreshFont = '释放刷新...';
        break;
      case 4:
        _refreshFont = '正在刷新...';
        break;
      default:
        _refreshFont = '下拉刷新...';
    }
    return (
      <View style={Styles.headerRefresh}>
        {gestureStatus === 4 ?
          <ActivityIndicator
            size={'small'}
            animating={true}
            color={'#75c5fe'}
            style={{marginRight: 10}}/> : null}
        <Text style={Styles.refreshFont}>{_refreshFont}</Text>
      </View>
    );
  }
}

const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
export default class Example extends Component {
  _timer = -1;
  data = [];

  constructor(props) {
    super(props)
    this.getData()
    this.state = {
      dataSource: ds.cloneWithRows(this.data),
    }
  }

  getData(init) {
    let total = 4;
    if (init) {
      this.data = [];
      total = Math.ceil(Math.random() * 17)
    }
    for (let i = 0; i < total; i++) {
      this.data.push('row' + Math.ceil(Math.random() * total));
    }
  }

  renderRow = (rowData, sectionID, rowID) => {
    return (
      <View
        style={Styles.flatListItem}>
        <Text style={Styles.font_3}>{rowData + ':' + rowID}</Text>
      </View>
    )
  }

  ItemSeparatorComponent = ({highlighted}) => {
    return <View style={[Styles.separator, {backgroundColor: highlighted ? '#FDFE3C' : '#feafea'}]}/>
  }

  renderItem = ({item, index, separators}) => {
    return (
      <TouchableHighlight
        onPress={() => console.log(item + ' pressed!!!')}
        activeOpacity={1}
        underlayColor={'#e8e8e8'}
        onShowUnderlay={separators.highlight}
        onHideUnderlay={separators.unhighlight}
        style={Styles.flatListItem}>
        <Text style={Styles.font_3}>{item + ':' + index}</Text>
      </TouchableHighlight>
    )
  }

  render() {
    return (
      <View style={Styles.wrap}>
        <View style={{height: 40, width: Dimensions.get('window').width, backgroundColor: '#d5c639'}}/>
        <RefresherFlatList
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          showsVerticalScrollIndicator={false}

          scrollComponent={'ListView'}

          enableHeaderRefresh={true}
          renderHeaderRefresh={(gestureStatus) => <HeaderRefresh gestureStatus={gestureStatus}/>}
          setHeaderHeight={100}
          onHeaderRefreshing={() => {
            clearTimeout(this._timer)
            this._timer = setTimeout(() => {
              this.getData(true)
              this.setState({
                dataSource: ds.cloneWithRows(this.data)
              }, () => {
                RefresherFlatList.headerRefreshDone()
              })
            }, 1000)
          }}

          enableFooterInfinite={true}
          renderFooterInfinite={(gestureStatus) => <FooterInfinite gestureStatus={gestureStatus}/>}
          setFooterHeight={60}
          onFooterInfiniting={() => {
            clearTimeout(this._timer)
            this._timer = setTimeout(() => {
              this.getData()
              this.setState({
                dataSource: ds.cloneWithRows(this.data)
              }, () => {
                RefresherFlatList.footerInfiniteDone()
              })
            }, 1000)
          }}
        />
      </View>
    )
    // return (
    //   <View style={Styles.wrap}>
    //     <View style={{height: 40, width: Dimensions.get('window').width, backgroundColor: '#142124'}}/>
    //     <RefresherFlatList
    //       data={this.data}
    //       ItemSeparatorComponent={this.ItemSeparatorComponent}
    //       renderItem={this.renderItem}
    //       keyExtractor={(item, index) => ('flatPTR_' + index)}
    //       showsVerticalScrollIndicator={false}
    //       contentContainerStyle={{backgroundColor: '#ffffff'}}
    //
    //       scrollComponent={'FlatList'}
    //
    //       enableHeaderRefresh={true}
    //       renderHeaderRefresh={(gestureStatus) => <HeaderRefresh gestureStatus={gestureStatus}/>}
    //       onHeaderRefreshing={() => {
    //         clearTimeout(this._timer)
    //         this._timer = setTimeout(() => {
    //           this.getData(true)
    //           this.setState({
    //             dataSource: ds.cloneWithRows(this.data)
    //           }, () => {
    //             RefresherFlatList.headerRefreshDone()
    //           })
    //         }, 5000)
    //       }}
    //
    //       enableFooterInfinite={true}
    //       renderFooterInfinite={(gestureStatus) => <FooterInfinite gestureStatus={gestureStatus}/>}
    //       onFooterInfiniting={() => {
    //         clearTimeout(this._timer)
    //         this._timer = setTimeout(() => {
    //           this.getData()
    //           this.setState({
    //             dataSource: ds.cloneWithRows(this.data)
    //           }, () => {
    //             RefresherFlatList.footerInfiniteDone()
    //           })
    //         }, 1000)
    //       }}
    //     />
    //   </View>
    // )
  }
}

const Styles = StyleSheet.create({
  wrap: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'center',
    backgroundColor: HBStyle.color.gray_bg
  },
  commonRowSC: {
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  commonColumnSS: {
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
  },
  protocolWrap: {
    width: Util.size.screen.width,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    backgroundColor: HBStyle.color.wwhite,
    marginTop: 15,
    borderTopWidth: 0.5,
    borderBottomWidth: 0.5,
    borderColor: HBStyle.color.common_gray_line
  },
  protocol: {
    width: Util.size.screen.width,
    height: 44,
    paddingHorizontal: 16,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  font_1: {
    fontSize: 16,
    color: HBStyle.color.text_black_w
  },
  font_2: {
    fontSize: 14,
    color: HBStyle.color.text_gray_w_main,
  },
  font_3: {
    fontSize: 15,
    color: HBStyle.color.text_black_w
  },
  font_4: {
    fontSize: 14,
    color: HBStyle.color.text_red_w
  },
  font_5: {
    fontSize: 12,
    color: HBStyle.color.text_white_w,
    backgroundColor: 'transparent'
  },
  font_creditor: {
    fontSize: 16,
    color: HBStyle.color.text_black_w,
    marginTop: 15,
  },
  font_creditorDetail: {
    fontSize: 15,
    color: HBStyle.color.text_gray_w_main,
    marginTop: 20,
  },
  retractFont: {
    fontSize: 15,
    color: HBStyle.color.text_white_w,
  },
  divideLine: {
    width: Util.size.screen.width,
    height: 0.5,
    backgroundColor: HBStyle.color.common_gray_line,
  },
  iconNext: {
    width: 7,
    height: 13
  },
  creditorWrap: {
    width: Util.size.screen.width,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    backgroundColor: HBStyle.color.wwhite,
    marginTop: 15,
    paddingLeft: 16,
    paddingRight: 14,
    borderTopWidth: 0.5,
    borderTopColor: HBStyle.color.common_gray_line
  },
  listViewWrap: {
    flex: 1,
    width: Util.size.screen.width,
    backgroundColor: HBStyle.color.gray_bg
  },
  listItem: {
    width: Util.size.screen.width,
    backgroundColor: HBStyle.color.wwhite,
    paddingVertical: 16,
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
  },
  itemWrap: {
    width: Util.size.screen.width,
    paddingHorizontal: 14,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  itemProgressWrap: {
    width: 126,
    height: 20,
    overflow: 'hidden',
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 7 * Util.size.screen.pixelRatio,
  },
  endLoadMore: {
    width: Util.size.screen.width,
    height: 40,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: HBStyle.color.gray_bg
  },
  loadMoreFont: {
    fontSize: 12,
    color: HBStyle.color.text_gray_w_main
  },
  headerRefresh: {
    width: Dimensions.get('window').width,
    height: 100,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center'
  },
  footerInfinite: {
    width: Dimensions.get('window').width,
    height: 60,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center'
  },
  refreshFont: {
    fontSize: 16,
    color: '#b84f35'
  },
  separator: {
    width: '100%',
    height: 0.5,
    marginLeft: 10
  },
  flatListItem: {
    width: Dimensions.get('window').width,
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: '#feafea'
  }
});<|endoftext|>


<commit_before>import moment from 'moment';
import React, { Component, PropTypes } from 'react';
import { Well } from 'react-bootstrap';

class ReservationInfo extends Component {
  getMaxPeriodText(maxPeriod) {
    if (!maxPeriod) {
      return null;
    }
    const asHours = moment.duration(maxPeriod).asHours();
    return (

        {`Varauksen maksimipituus: ${asHours} tuntia`}

    );
  }

  getMaxReservationsPerUserText(maxReservationsPerUser) {
    if (!maxReservationsPerUser) {
      return null;
    }
    return (

        {`Maksimimäärä varauksia per käyttäjä: ${maxReservationsPerUser}`}

    );
  }

  render() {
    const { resource } = this.props;

    return (
      <Well>
        {this.getMaxPeriodText(resource.maxPeriod)}
        {this.getMaxReservationsPerUserText(resource.maxReservationsPerUser)}
      </Well>
    );
  }
}

ReservationInfo.propTypes = {
  resource: PropTypes.object.isRequired,
};

export default ReservationInfo;
<commit_msg>Show reservation instructions on reservation page<commit_after>import moment from 'moment';
import React, { Component, PropTypes } from 'react';
import { Well } from 'react-bootstrap';

class ReservationInfo extends Component {
  renderMaxPeriodText(maxPeriod) {
    if (!maxPeriod) {
      return null;
    }
    const asHours = moment.duration(maxPeriod).asHours();
    return (

        {`Varauksen maksimipituus: ${asHours} tuntia`}

    );
  }

  renderMaxReservationsPerUserText(maxReservationsPerUser) {
    if (!maxReservationsPerUser) {
      return null;
    }
    return (

        {`Maksimimäärä varauksia per käyttäjä: ${maxReservationsPerUser}`}

    );
  }

  renderReservationInfoText() {
    const mockReservationInfo = `
      Lorem ipsum dolor sit amet, id odio ludus torquatos per, eripuit apeirian deseruisse eos no.
      Mel ex aeque oporteat, sit nobis homero sensibus ea. Te eam porro atomorum philosophia.
      Invenire referrentur ei vim. Sed mollis ponderum ullamcorper ea, sit aliquid deseruisse
      incorrupte id, et qui probo consequat constituto.
    `;
    return (

{mockReservationInfo}

    );
  }

  render() {
    const { resource } = this.props;

    return (
      <Well>

Ohjeet varaamiseen

        {this.renderReservationInfoText()}
        {this.renderMaxPeriodText(resource.maxPeriod)}
        {this.renderMaxReservationsPerUserText(resource.maxReservationsPerUser)}
      </Well>
    );
  }
}

ReservationInfo.propTypes = {
  resource: PropTypes.object.isRequired,
};

export default ReservationInfo;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
    errorHandler = require('./errors.server.controller'),
    mongoose = require('mongoose'),
    passport = require('passport'),
    Messages = mongoose.model('Messages'),
    User = mongoose.model('User');

/**
 * Create a Message
 */
exports.sendMessage = function(req, res) {
    var message = new Messages(req.body);

    //check if the user the message is sent to exists
    User.find({username : message.userTo}, function (err, docs) {
        if (docs.length){
            //the user exists
            message.save(function (err) {
                if (err) {
                    return res.status(400).send({
                        message: errorHandler.getErrorMessage(err)
                    });
                }
                else {
                    res.json(message);
                }
            });
        }else{
            return res.status(400).send({
               message: 'User does not exist'
            });
        }
    });

};

/**
 * Show the current Message
 */
exports.read = function(req, res) {

};

/**
 * Update a Message
 */
exports.update = function(req, res) {

};

/**
 * Delete an Message
 */
exports.delete = function(req, res) {

};

/**
 * Get a list of Messages
 */
exports.getMessageList = function(req, res) {
    if(req.user !== undefined) {
        Messages.find({ $query: {userTo: req.user.username}, $orderby: { dateSent : -1 } }, function (err, results) {
            res.json(results);
        });
    }
    else {
        return res.status(400).send({
            message: 'You must be logged in to view inbox.'
        });
    }
};
<commit_msg>updated messages controller to handle login errors<commit_after>'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
    errorHandler = require('./errors.server.controller'),
    mongoose = require('mongoose'),
    passport = require('passport'),
    Messages = mongoose.model('Messages'),
    User = mongoose.model('User');

/**
 * Create a Message
 */
exports.sendMessage = function(req, res) {
    var message = new Messages(req.body);

    //check if the user the message is sent to exists
    if(message.userFrom){
        User.find({username : message.userTo}, function (err, docs) {
            if (docs.length){
                //the user exists
                message.save(function (err) {
                    if (err) {
                        return res.status(400).send({
                            message: errorHandler.getErrorMessage(err)
                        });
                    }
                    else {
                        res.json(message);
                    }
                });
            }else{
                return res.status(400).send({
                    message: 'User does not exist'
                });
            }
        });
    }
    else{
        res.status(401).send({
            message: 'Not logged in'
        });
    }
};

/**
 * Show the current Message
 */
exports.read = function(req, res) {

};

/**
 * Update a Message
 */
exports.update = function(req, res) {

};

/**
 * Delete an Message
 */
exports.delete = function(req, res) {

};

/**
 * Get a list of Messages
 */
exports.getMessageList = function(req, res) {
    if(req.user !== undefined) {
        Messages.find({ $query: {userTo: req.user.username}, $orderby: { dateSent : -1 } }, function (err, results) {
            res.json(results);
        });
    }
    else {
        return res.status(400).send({
            message: 'You must be logged in to view inbox.'
        });
    }
};
<|endoftext|>


<commit_before>export function isCloudFileConnected(cloudStatus) {
  return ['loaded', 'loaded-read-only', 'dirty', 'saving', 'saved'].includes(this.cloudStatus);
}
<commit_msg>Fix bug<commit_after>export function isCloudFileConnected(cloudStatus) {
  return ['loaded', 'loaded-read-only', 'dirty', 'saving', 'saved'].includes(cloudStatus);
}
<|endoftext|>


<commit_before>//POSSIBLE GAME STRUCTURE
//Game object contains pieces containers and board
//it's "main method" looks for a div with the id "container"
//and puts itself in the container
//TODO make game piece container group
//     for each set of game pieces
//     position them correctly
//TODO DRY up existing code
//         create class for lines
//TODO make game_board it's own object
//     game_board.add_to_layer(layer)
//TODO are these good variable names?
//TODO get rid of magic numbers?
//TODO make script a separate file

function GameBoard(xLoc, yLoc, sideLength){

	this.x = xLoc;
	this.y = yLoc;
	this.sideLength = sideLength;

	/*
	The game board is built out of KinetcJS primitives, namely:
	3 successively smaller boxes, each on top of the other
	1 horizontal line crossing the middle of the board, covered
	by the smallest box.
	1 vertical line crossing the middle of the board, covered
	by the smallest box.
	24 dots on the corners and midpoints of the sides of each box.

	None of these primitives are draggable
	*/

	var stageContainer = new Kinetic.Stage({
		container: 'container',
		width: 500,
		height: 600
	});

	var game_board = new Kinetic.Group({
		x: this.x,
		y: this.y
	});

	var gameBoardLayer = new Kinetic.Layer();

	this.drawBoxes(game_board);

	gameBoardLayer.add(game_board);

	this.drawGamePieces(gameBoardLayer);

	stageContainer.add(gameBoardLayer);

}

GameBoard.prototype.drawBoxes = function(game_board) {
	//The outer for-loop draws each box and the the vertical and
	//horizontal lines.
	for(var i = 0; i < 3; i++) {
		var box_xy_offset = i * 50;
		var box_side_length = this.sideLength - (i * 100);

	//This object is created in each iteration of the for-loop
	//and is made successively smaller.
	var box = new Kinetic.Rect({
		x: box_xy_offset,
		y: box_xy_offset,
		width: box_side_length,
		height: box_side_length,
		fill: 'white',
		stroke: 'black',
		strokeWidth: 4
	});

	//This if statement checks to see if the second box has
	//been drawn. If it has then the horizontal and vertical
	//lines are drawn.
	if(i == 2) {
		this.drawLines(game_board);
	}
	game_board.add(box);

	this.drawSpaces(game_board, box_xy_offset, box_side_length);
	}
}

GameBoard.prototype.drawLines = function(game_board) {
	var  vertical_line = new Kinetic.Line({
		points: [this.sideLength/2, 0, this.sideLength/2, this.sideLength],
		stroke: 'black',
		strokeWidth: 4
	});

	var  horizontal_line = new Kinetic.Line({
		points: [0, this.sideLength/2, this.sideLength, this.sideLength/2],
		stroke: 'black',
		strokeWidth: 4
	});

	game_board.add(horizontal_line);
	game_board.add(vertical_line);
}

GameBoard.prototype.drawSpaces = function(game_board, box_xy_offset, box_side_length) {
	//This function helps the for-loop below determine the
	//x or y position for each of the 24 dots
	var get_coordinate = function(index) {
		return box_xy_offset + index * .5 * box_side_length;
	}
	//This for-loop draws the 24 dots
	//j helps determine the x position of the dot
	//k helps determine the y position of the dot
	for(var j = 0; j < 3; j++) {
		for(var k = 0; k < 3; k++) {
			if(j != 1 || k != 1) {
				var dot = new Kinetic.Circle({
					x: get_coordinate(j),
					y: get_coordinate(k),
					radius: 10,
					fill: 'black'
				});
				game_board.add(dot);
			}
		}
	}

}

GameBoard.prototype.drawGamePieces = function(layer) {
	//This for-loop creates all game pieces

	var whitePieceArray = new Array();
	var redPieceArray = new Array();

	for(var t = 0; t < 9; t++) {

		//the t == 8 results in a boolean value, used to set the "draggable" option
		whitePieceArray.push(new GamePiece(this.x + 15 * t, this.y - 50, 20, 'white', 'black', 2, t==8, layer));
		redPieceArray.push(new GamePiece(this.x + this.sideLength - 15 * t, this.y - 50, 20, 'red', 'black', 2, t==8, layer));

		//layer.add(whitePieceArray[t].game_piece);
		//layer.add(redPieceArray[t].game_piece);

	}

}

function GamePiece(xPos, yPos, radius, fill, stroke, strokeWidth, draggable, layer){

	var game_piece = new Kinetic.Circle({
		x: xPos,
		y: yPos,
		radius: radius,
		fill: fill,
		stroke: stroke,
		strokeWidth: strokeWidth,
		draggable: draggable
	});

	layer.add(game_piece);
}

var gameBoard = new GameBoard(50, 80, 400);
<commit_msg>Update NineManMorris.js<commit_after>//POSSIBLE GAME STRUCTURE
//Game object contains pieces containers and board
//it's "main method" looks for a div with the id "container"
//and puts itself in the container
//TODO make game piece container group
//     for each set of game pieces
//     position them correctly
//TODO DRY up existing code
//         create class for lines
//TODO make game_board it's own object
//     game_board.add_to_layer(layer)
//TODO are these good variable names?
//TODO get rid of magic numbers?
//TODO make script a separate file

function GameBoard(xLoc, yLoc, sideLength){

	this.x = xLoc;
	this.y = yLoc;
	this.sideLength = sideLength;

	/*
	The game board is built out of KinetcJS primitives, namely:
	3 successively smaller boxes, each on top of the other
	1 horizontal line crossing the middle of the board, covered
	by the smallest box.
	1 vertical line crossing the middle of the board, covered
	by the smallest box.
	24 dots on the corners and midpoints of the sides of each box.

	None of these primitives are draggable
	*/

	var stageContainer = new Kinetic.Stage({
		container: 'container',
		width: 500,
		height: 600
	});

	var game_board = new Kinetic.Group({
		x: this.x,
		y: this.y
	});

	var gameBoardLayer = new Kinetic.Layer();

	this.drawBoxes(game_board);

	gameBoardLayer.add(game_board);

	this.drawGamePieces(gameBoardLayer);

	stageContainer.add(gameBoardLayer);

}

GameBoard.prototype.drawBoxes = function(game_board) {
	//The outer for-loop draws each box and the the vertical and
	//horizontal lines.
	for(var i = 0; i < 3; i++) {
		var box_xy_offset = i * 50;
		var box_side_length = this.sideLength - (i * 100);

	//This object is created in each iteration of the for-loop
	//and is made successively smaller.
	var box = new Kinetic.Rect({
		x: box_xy_offset,
		y: box_xy_offset,
		width: box_side_length,
		height: box_side_length,
		fill: 'white',
		stroke: 'black',
		strokeWidth: 4
	});

	//This if statement checks to see if the second box has
	//been drawn. If it has then the horizontal and vertical
	//lines are drawn.
	if(i == 2) {
		this.drawLines(game_board);
	}
	game_board.add(box);

	this.drawSpaces(game_board, box_xy_offset, box_side_length);
	}
}

GameBoard.prototype.drawLines = function(game_board) {
	var  vertical_line = new Kinetic.Line({
		points: [this.sideLength/2, 0, this.sideLength/2, this.sideLength],
		stroke: 'black',
		strokeWidth: 4
	});

	var  horizontal_line = new Kinetic.Line({
		points: [0, this.sideLength/2, this.sideLength, this.sideLength/2],
		stroke: 'black',
		strokeWidth: 4
	});

	game_board.add(horizontal_line);
	game_board.add(vertical_line);
}

GameBoard.prototype.drawSpaces = function(game_board, box_xy_offset, box_side_length) {
	//This function helps the for-loop below determine the
	//x or y position for each of the 24 dots
	var get_coordinate = function(index) {
		return box_xy_offset + index * .5 * box_side_length;
	}
	//This for-loop draws the 24 dots
	//j helps determine the x position of the dot
	//k helps determine the y position of the dot
	for(var j = 0; j < 3; j++) {
		for(var k = 0; k < 3; k++) {
			if(j != 1 || k != 1) {
				var dot = new Kinetic.Circle({
					x: get_coordinate(j),
					y: get_coordinate(k),
					radius: 10,
					fill: 'black'
				});
				game_board.add(dot);
			}
		}
	}

}

GameBoard.prototype.drawGamePieces = function(layer) {
	//This for-loop creates all game pieces

	var whitePieceArray = new Array();
	var redPieceArray = new Array();

	for(var t = 0; t < 9; t++) {

		//the t == 8 results in a boolean value, used to set the "draggable" option
		whitePieceArray.push(new GamePiece(this.x + 15 * t, this.y - 50, 20, 'white', 'black', 2, t==8, layer));
		redPieceArray.push(new GamePiece(this.x + this.sideLength - 15 * t, this.y - 50, 20, 'red', 'black', 2, t==8, layer));

		//layer.add(whitePieceArray[t].game_piece);
		//layer.add(redPieceArray[t].game_piece);

	}

}

function GamePiece(xPos, yPos, radius, fill, stroke, strokeWidth, draggable, layer){

	function writeMessage(message) {
        text.text(message);
        layer.draw();
    }

	var text = new Kinetic.Text({
        x: 200,
        y: 250,
        fontFamily: 'Calibri',
        fontSize: 24,
        text: '',
        fill: 'black'
      });

	var game_piece = new Kinetic.Circle({
		x: xPos,
		y: yPos,
		radius: radius,
		fill: fill,
		stroke: stroke,
		strokeWidth: strokeWidth,
		draggable: draggable
	});

	game_piece.on('dragstart', function() {
        writeMessage('dragstart');
    });
	game_piece.on('dragend', function() {
        writeMessage('dragend');
    });

	layer.add(text);
	layer.add(game_piece);
}

var gameBoard = new GameBoard(50, 80, 400);
<|endoftext|>


<commit_before>/* @flow */
'use strict';

import type {Background} from './parsing/background';
import type {Border} from './parsing/border';
import type {BorderRadius} from './parsing/borderRadius';
import type {DisplayBit} from './parsing/display';
import type {Float} from './parsing/float';
import type {Font} from './parsing/font';
import type {Overflow} from './parsing/overflow';
import type {Padding} from './parsing/padding';
import type {Position} from './parsing/position';
import type {TextShadow} from './parsing/textShadow';
import type {TextTransform} from './parsing/textTransform';
import type {TextDecoration} from './parsing/textDecoration';
import type {Transform} from './parsing/transform';
import type {Visibility} from './parsing/visibility';
import type {zIndex} from './parsing/zIndex';

import type {Bounds, BoundCurves} from './Bounds';
import type ImageLoader from './ImageLoader';
import type {Path} from './drawing/Path';
import type TextContainer from './TextContainer';

import Color from './Color';

import {contains} from './Util';
import {parseBackground} from './parsing/background';
import {parseBorder} from './parsing/border';
import {parseBorderRadius} from './parsing/borderRadius';
import {parseDisplay, DISPLAY} from './parsing/display';
import {parseCSSFloat, FLOAT} from './parsing/float';
import {parseFont} from './parsing/font';
import {parseLetterSpacing} from './parsing/letterSpacing';
import {parseOverflow, OVERFLOW} from './parsing/overflow';
import {parsePadding} from './parsing/padding';
import {parsePosition, POSITION} from './parsing/position';
import {parseTextDecoration} from './parsing/textDecoration';
import {parseTextShadow} from './parsing/textShadow';
import {parseTextTransform} from './parsing/textTransform';
import {parseTransform} from './parsing/transform';
import {parseVisibility, VISIBILITY} from './parsing/visibility';
import {parseZIndex} from './parsing/zIndex';

import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
import {
    INPUT_BACKGROUND,
    INPUT_BORDERS,
    INPUT_COLOR,
    getInputBorderRadius,
    reformatInputBounds
} from './Input';

type StyleDeclaration = {
    background: Background,
    border: Array<Border>,
    borderRadius: Array<BorderRadius>,
    color: Color,
    display: DisplayBit,
    float: Float,
    font: Font,
    letterSpacing: number,
    opacity: number,
    overflow: Overflow,
    padding: Padding,
    position: Position,
    textDecoration: TextDecoration | null,
    textShadow: Array<TextShadow> | null,
    textTransform: TextTransform,
    transform: Transform,
    visibility: Visibility,
    zIndex: zIndex
};

export default class NodeContainer {
    name: ?string;
    parent: ?NodeContainer;
    style: StyleDeclaration;
    childNodes: Array<TextContainer | Path>;
    bounds: Bounds;
    curvedBounds: BoundCurves;
    image: ?string;
    index: number;

    constructor(
        node: HTMLElement,
        parent: ?NodeContainer,
        imageLoader: ImageLoader,
        index: number
    ) {
        this.parent = parent;
        this.index = index;
        this.childNodes = [];
        const defaultView = node.ownerDocument.defaultView;
        const style = defaultView.getComputedStyle(node, null);
        const display = parseDisplay(style.display);

        const IS_INPUT = node.type === 'radio' || node.type === 'checkbox';

        const position = parsePosition(style.position);

        this.style = {
            background: IS_INPUT ? INPUT_BACKGROUND : parseBackground(style, imageLoader),
            border: IS_INPUT ? INPUT_BORDERS : parseBorder(style),
            borderRadius:
                (node instanceof defaultView.HTMLInputElement ||
                    node instanceof HTMLInputElement) &&
                IS_INPUT
                    ? getInputBorderRadius(node)
                    : parseBorderRadius(style),
            color: IS_INPUT ? INPUT_COLOR : new Color(style.color),
            display: display,
            float: parseCSSFloat(style.float),
            font: parseFont(style),
            letterSpacing: parseLetterSpacing(style.letterSpacing),
            opacity: parseFloat(style.opacity),
            overflow: parseOverflow(style.overflow),
            padding: parsePadding(style),
            position: position,
            textDecoration: parseTextDecoration(style),
            textShadow: parseTextShadow(style.textShadow),
            textTransform: parseTextTransform(style.textTransform),
            transform: parseTransform(style),
            visibility: parseVisibility(style.visibility),
            zIndex: position !== POSITION.STATIC ? parseZIndex(style.zIndex) : 0
        };

        if (this.isTransformed()) {
            // getBoundingClientRect provides values post-transform, we want them without the transformation
            node.style.transform = 'matrix(1,0,0,1,0,0)';
        }

        this.image = getImage(node, imageLoader);
        this.bounds = IS_INPUT ? reformatInputBounds(parseBounds(node)) : parseBounds(node);
        this.curvedBounds = parseBoundCurves(
            this.bounds,
            this.style.border,
            this.style.borderRadius
        );

        if (__DEV__) {
            this.name = `${node.tagName.toLowerCase()}${node.id
                ? `#${node.id}`
                : ''}${node.className.split(' ').map(s => (s.length ? `.${s}` : '')).join('')}`;
        }
    }
    getClipPaths(): Array<Path> {
        const parentClips = this.parent ? this.parent.getClipPaths() : [];
        const isClipped =
            this.style.overflow === OVERFLOW.HIDDEN || this.style.overflow === OVERFLOW.SCROLL;

        return isClipped
            ? parentClips.concat([calculatePaddingBoxPath(this.curvedBounds)])
            : parentClips;
    }
    isInFlow(): boolean {
        return this.isRootElement() && !this.isFloating() && !this.isAbsolutelyPositioned();
    }
    isVisible(): boolean {
        return (
            !contains(this.style.display, DISPLAY.NONE) &&
            this.style.opacity > 0 &&
            this.style.visibility === VISIBILITY.VISIBLE
        );
    }
    isAbsolutelyPositioned(): boolean {
        return this.style.position !== POSITION.STATIC && this.style.position !== POSITION.RELATIVE;
    }
    isPositioned(): boolean {
        return this.style.position !== POSITION.STATIC;
    }
    isFloating(): boolean {
        return this.style.float !== FLOAT.NONE;
    }
    isRootElement(): boolean {
        return this.parent === null;
    }
    isTransformed(): boolean {
        return this.style.transform !== null;
    }
    isPositionedWithZIndex(): boolean {
        return this.isPositioned() && !this.style.zIndex.auto;
    }
    isInlineLevel(): boolean {
        return (
            contains(this.style.display, DISPLAY.INLINE) ||
            contains(this.style.display, DISPLAY.INLINE_BLOCK) ||
            contains(this.style.display, DISPLAY.INLINE_FLEX) ||
            contains(this.style.display, DISPLAY.INLINE_GRID) ||
            contains(this.style.display, DISPLAY.INLINE_LIST_ITEM) ||
            contains(this.style.display, DISPLAY.INLINE_TABLE)
        );
    }
    isInlineBlockOrInlineTable(): boolean {
        return (
            contains(this.style.display, DISPLAY.INLINE_BLOCK) ||
            contains(this.style.display, DISPLAY.INLINE_TABLE)
        );
    }
}

const getImage = (node: HTMLElement, imageLoader: ImageLoader): ?string => {
    switch (node.tagName) {
        case 'IMG':
            // $FlowFixMe
            return imageLoader.loadImage(node.currentSrc || node.src);
        case 'CANVAS':
            // $FlowFixMe
            return imageLoader.loadCanvas(node);
    }

    return null;
};
<commit_msg>Fix zIndex value<commit_after>/* @flow */
'use strict';

import type {Background} from './parsing/background';
import type {Border} from './parsing/border';
import type {BorderRadius} from './parsing/borderRadius';
import type {DisplayBit} from './parsing/display';
import type {Float} from './parsing/float';
import type {Font} from './parsing/font';
import type {Overflow} from './parsing/overflow';
import type {Padding} from './parsing/padding';
import type {Position} from './parsing/position';
import type {TextShadow} from './parsing/textShadow';
import type {TextTransform} from './parsing/textTransform';
import type {TextDecoration} from './parsing/textDecoration';
import type {Transform} from './parsing/transform';
import type {Visibility} from './parsing/visibility';
import type {zIndex} from './parsing/zIndex';

import type {Bounds, BoundCurves} from './Bounds';
import type ImageLoader from './ImageLoader';
import type {Path} from './drawing/Path';
import type TextContainer from './TextContainer';

import Color from './Color';

import {contains} from './Util';
import {parseBackground} from './parsing/background';
import {parseBorder} from './parsing/border';
import {parseBorderRadius} from './parsing/borderRadius';
import {parseDisplay, DISPLAY} from './parsing/display';
import {parseCSSFloat, FLOAT} from './parsing/float';
import {parseFont} from './parsing/font';
import {parseLetterSpacing} from './parsing/letterSpacing';
import {parseOverflow, OVERFLOW} from './parsing/overflow';
import {parsePadding} from './parsing/padding';
import {parsePosition, POSITION} from './parsing/position';
import {parseTextDecoration} from './parsing/textDecoration';
import {parseTextShadow} from './parsing/textShadow';
import {parseTextTransform} from './parsing/textTransform';
import {parseTransform} from './parsing/transform';
import {parseVisibility, VISIBILITY} from './parsing/visibility';
import {parseZIndex} from './parsing/zIndex';

import {parseBounds, parseBoundCurves, calculatePaddingBoxPath} from './Bounds';
import {
    INPUT_BACKGROUND,
    INPUT_BORDERS,
    INPUT_COLOR,
    getInputBorderRadius,
    reformatInputBounds
} from './Input';

type StyleDeclaration = {
    background: Background,
    border: Array<Border>,
    borderRadius: Array<BorderRadius>,
    color: Color,
    display: DisplayBit,
    float: Float,
    font: Font,
    letterSpacing: number,
    opacity: number,
    overflow: Overflow,
    padding: Padding,
    position: Position,
    textDecoration: TextDecoration | null,
    textShadow: Array<TextShadow> | null,
    textTransform: TextTransform,
    transform: Transform,
    visibility: Visibility,
    zIndex: zIndex
};

export default class NodeContainer {
    name: ?string;
    parent: ?NodeContainer;
    style: StyleDeclaration;
    childNodes: Array<TextContainer | Path>;
    bounds: Bounds;
    curvedBounds: BoundCurves;
    image: ?string;
    index: number;

    constructor(
        node: HTMLElement,
        parent: ?NodeContainer,
        imageLoader: ImageLoader,
        index: number
    ) {
        this.parent = parent;
        this.index = index;
        this.childNodes = [];
        const defaultView = node.ownerDocument.defaultView;
        const style = defaultView.getComputedStyle(node, null);
        const display = parseDisplay(style.display);

        const IS_INPUT = node.type === 'radio' || node.type === 'checkbox';

        const position = parsePosition(style.position);

        this.style = {
            background: IS_INPUT ? INPUT_BACKGROUND : parseBackground(style, imageLoader),
            border: IS_INPUT ? INPUT_BORDERS : parseBorder(style),
            borderRadius:
                (node instanceof defaultView.HTMLInputElement ||
                    node instanceof HTMLInputElement) &&
                IS_INPUT
                    ? getInputBorderRadius(node)
                    : parseBorderRadius(style),
            color: IS_INPUT ? INPUT_COLOR : new Color(style.color),
            display: display,
            float: parseCSSFloat(style.float),
            font: parseFont(style),
            letterSpacing: parseLetterSpacing(style.letterSpacing),
            opacity: parseFloat(style.opacity),
            overflow: parseOverflow(style.overflow),
            padding: parsePadding(style),
            position: position,
            textDecoration: parseTextDecoration(style),
            textShadow: parseTextShadow(style.textShadow),
            textTransform: parseTextTransform(style.textTransform),
            transform: parseTransform(style),
            visibility: parseVisibility(style.visibility),
            zIndex: parseZIndex(position !== POSITION.STATIC ? style.zIndex : 'auto')
        };

        if (this.isTransformed()) {
            // getBoundingClientRect provides values post-transform, we want them without the transformation
            node.style.transform = 'matrix(1,0,0,1,0,0)';
        }

        this.image = getImage(node, imageLoader);
        this.bounds = IS_INPUT ? reformatInputBounds(parseBounds(node)) : parseBounds(node);
        this.curvedBounds = parseBoundCurves(
            this.bounds,
            this.style.border,
            this.style.borderRadius
        );

        if (__DEV__) {
            this.name = `${node.tagName.toLowerCase()}${node.id
                ? `#${node.id}`
                : ''}${node.className.split(' ').map(s => (s.length ? `.${s}` : '')).join('')}`;
        }
    }
    getClipPaths(): Array<Path> {
        const parentClips = this.parent ? this.parent.getClipPaths() : [];
        const isClipped =
            this.style.overflow === OVERFLOW.HIDDEN || this.style.overflow === OVERFLOW.SCROLL;

        return isClipped
            ? parentClips.concat([calculatePaddingBoxPath(this.curvedBounds)])
            : parentClips;
    }
    isInFlow(): boolean {
        return this.isRootElement() && !this.isFloating() && !this.isAbsolutelyPositioned();
    }
    isVisible(): boolean {
        return (
            !contains(this.style.display, DISPLAY.NONE) &&
            this.style.opacity > 0 &&
            this.style.visibility === VISIBILITY.VISIBLE
        );
    }
    isAbsolutelyPositioned(): boolean {
        return this.style.position !== POSITION.STATIC && this.style.position !== POSITION.RELATIVE;
    }
    isPositioned(): boolean {
        return this.style.position !== POSITION.STATIC;
    }
    isFloating(): boolean {
        return this.style.float !== FLOAT.NONE;
    }
    isRootElement(): boolean {
        return this.parent === null;
    }
    isTransformed(): boolean {
        return this.style.transform !== null;
    }
    isPositionedWithZIndex(): boolean {
        return this.isPositioned() && !this.style.zIndex.auto;
    }
    isInlineLevel(): boolean {
        return (
            contains(this.style.display, DISPLAY.INLINE) ||
            contains(this.style.display, DISPLAY.INLINE_BLOCK) ||
            contains(this.style.display, DISPLAY.INLINE_FLEX) ||
            contains(this.style.display, DISPLAY.INLINE_GRID) ||
            contains(this.style.display, DISPLAY.INLINE_LIST_ITEM) ||
            contains(this.style.display, DISPLAY.INLINE_TABLE)
        );
    }
    isInlineBlockOrInlineTable(): boolean {
        return (
            contains(this.style.display, DISPLAY.INLINE_BLOCK) ||
            contains(this.style.display, DISPLAY.INLINE_TABLE)
        );
    }
}

const getImage = (node: HTMLElement, imageLoader: ImageLoader): ?string => {
    switch (node.tagName) {
        case 'IMG':
            // $FlowFixMe
            return imageLoader.loadImage(node.currentSrc || node.src);
        case 'CANVAS':
            // $FlowFixMe
            return imageLoader.loadCanvas(node);
    }

    return null;
};
<|endoftext|>


<commit_before>import * as types from './types'
import { makeProfileZoneFile } from 'blockstack'
import { IdentityActions } from  '../identity'
import { uploadProfile } from '../../../account/utils'
import {
  signProfileForUpload, authorizationHeaderValue
} from '../../../utils'
import { DEFAULT_PROFILE } from '../../../utils/profile-utils'
import log4js from 'log4js'

const logger = log4js.getLogger('profiles/store/registration/actions.js')

function profileUploading() {
  return {
    type: types.PROFILE_UPLOADING
  }
}

function profileUploadError(error) {
  return {
    type: types.PROFILE_UPLOAD_ERROR,
    error
  }
}

function registrationBeforeSubmit() {
  return {
    type: types.REGISTRATION_BEFORE_SUBMIT
  }
}

function registrationSubmitting() {
  return {
    type: types.REGISTRATION_SUBMITTING
  }
}

function registrationSubmitted() {
  return {
    type: types.REGISTRATION_SUBMITTED
  }
}

function registrationError(error) {
  return {
    type: types.REGISTRATION_ERROR,
    error
  }
}

function beforeRegister() {
  logger.trace('beforeRegister')
  return dispatch => {
    dispatch(registrationBeforeSubmit())
  }
}

function registerName(api, domainName, ownerAddress, keypair) {
  logger.trace(`registerName: domainName: ${domainName}`)
  return dispatch => {
    logger.debug(`Signing a blank default profile for ${domainName}`)

    const signedProfileTokenData = signProfileForUpload(DEFAULT_PROFILE, keypair)

    dispatch(profileUploading())
    logger.trace(`Uploading ${domainName} profile...`)
    return uploadProfile(api, domainName, signedProfileTokenData, true).then((profileUrl) => {
      logger.trace(`Uploading ${domainName} profiled succeeded.`)
      const tokenFileUrl = profileUrl
      logger.debug(`tokenFileUrl: ${tokenFileUrl}`)

      logger.trace('Making profile zonefile...')
      const zoneFile = makeProfileZoneFile(domainName, tokenFileUrl)

      const requestHeaders = {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        Authorization: authorizationHeaderValue(api.coreAPIPassword)
      }

      const registrationRequestBody = JSON.stringify({
        name: domainName,
        owner_address: ownerAddress,
        zonefile: zoneFile,
        min_confs: 0,
        unsafe: true
      })

      // Core registers with an uncompressed address,
      // browser expects compressed addresses,
      // we need to add a suffix to indicate to core
      // that it should use a compressed addresses
      // see https://en.bitcoin.it/wiki/Wallet_import_format
      // and https://github.com/blockstack/blockstack-browser/issues/607
      const compressedPublicKeySuffix = '01'

      const setOwnerKeyRequestBody = JSON.stringify(`${keypair.key}${compressedPublicKeySuffix}`)

      dispatch(registrationSubmitting())
      logger.trace(`Submitting registration for ${domainName} to Core node at ${api.registerUrl}`)

      const setOwnerKeyUrl = `http://${api.coreHost}:${api.corePort}/v1/wallet/keys/owner`

      return fetch(setOwnerKeyUrl, {
        method: 'PUT',
        headers: requestHeaders,
        body: setOwnerKeyRequestBody
      })
        .then(() => fetch(api.registerUrl, {
          method: 'POST',
          headers: requestHeaders,
          body: registrationRequestBody
        })
        .then((response) => response.text())
        .then((responseText) => JSON.parse(responseText))
        .then((responseJson) => {
          if (responseJson.error) {
            logger.error(responseJson.error)
            dispatch(registrationError(responseJson.error))
          } else {
            logger.debug(`Successfully submitted registration for ${domainName}`)
            dispatch(registrationSubmitted())
            const addingUsername = true
            dispatch(IdentityActions.createNewIdentityFromDomain(domainName,
              ownerAddress, addingUsername))
          }
        })
        .catch((error) => {
          logger.error('registerName: error POSTing regitsration to Core', error)
          dispatch(registrationError(error))
        })
      ).catch((error) => {
        logger.error('registerName: error setting owner key', error)
        dispatch(registrationError(error))
      })
    }).catch((error) => {
      logger.error('registerName: error uploading profile', error)
      dispatch(profileUploadError(error))
    })
  }
}

const RegistrationActions = {
  profileUploading,
  profileUploadError,
  beforeRegister,
  registerName,
  registrationBeforeSubmit,
  registrationSubmitting,
  registrationSubmitted,
  registrationError
}

export default RegistrationActions
<commit_msg>refactor registerName action to support subdomains #664<commit_after>import * as types from './types'
import { makeProfileZoneFile } from 'blockstack'
import { IdentityActions } from  '../identity'
import { uploadProfile } from '../../../account/utils'
import {
  signProfileForUpload, authorizationHeaderValue
} from '../../../utils'
import { DEFAULT_PROFILE } from '../../../utils/profile-utils'
import { isSubdomain, getNameSuffix } from '../../../utils/name-utils'
import log4js from 'log4js'

const logger = log4js.getLogger('profiles/store/registration/actions.js')

function profileUploading() {
  return {
    type: types.PROFILE_UPLOADING
  }
}

function profileUploadError(error) {
  return {
    type: types.PROFILE_UPLOAD_ERROR,
    error
  }
}

function registrationBeforeSubmit() {
  return {
    type: types.REGISTRATION_BEFORE_SUBMIT
  }
}

function registrationSubmitting() {
  return {
    type: types.REGISTRATION_SUBMITTING
  }
}

function registrationSubmitted() {
  return {
    type: types.REGISTRATION_SUBMITTED
  }
}

function registrationError(error) {
  return {
    type: types.REGISTRATION_ERROR,
    error
  }
}

function beforeRegister() {
  logger.trace('beforeRegister')
  return dispatch => {
    dispatch(registrationBeforeSubmit())
  }
}

function setOwnerKey(setOwnerKeyUrl, requestHeaders, keypair, nameIsSubdomain) {
  return new Promise((resolve, reject) => {
    if (nameIsSubdomain) {
      logger.debug('setOwnerKey: skipping setting core owner key for subdomain')
      resolve()
      return
    }

    // Core registers with an uncompressed address,
    // browser expects compressed addresses,
    // we need to add a suffix to indicate to core
    // that it should use a compressed addresses
    // see https://en.bitcoin.it/wiki/Wallet_import_format
    // and https://github.com/blockstack/blockstack-browser/issues/607
    const compressedPublicKeySuffix = '01'

    const setOwnerKeyRequestBody = JSON.stringify(`${keypair.key}${compressedPublicKeySuffix}`)

    logger.debug('setOwnerKey: setting core owner key')

    fetch(setOwnerKeyUrl, {
      method: 'PUT',
      headers: requestHeaders,
      body: setOwnerKeyRequestBody
    })
    .then(() => resolve())
    .catch((error) => reject(error))
  })
}

function registerName(api, domainName, ownerAddress, keypair) {
  logger.trace(`registerName: domainName: ${domainName}`)
  return dispatch => {
    logger.debug(`Signing a blank default profile for ${domainName}`)

    const signedProfileTokenData = signProfileForUpload(DEFAULT_PROFILE, keypair)

    dispatch(profileUploading())
    logger.trace(`Uploading ${domainName} profile...`)
    return uploadProfile(api, domainName, signedProfileTokenData, true).then((profileUrl) => {
      logger.trace(`Uploading ${domainName} profiled succeeded.`)
      const tokenFileUrl = profileUrl
      logger.debug(`tokenFileUrl: ${tokenFileUrl}`)

      logger.trace('Making profile zonefile...')
      const zoneFile = makeProfileZoneFile(domainName, tokenFileUrl)

      const requestHeaders = {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        Authorization: authorizationHeaderValue(api.coreAPIPassword)
      }

      const nameIsSubdomain = isSubdomain(domainName)
      let registerUrl = api.registerUrl
      let nameSuffix = null
      if (nameIsSubdomain) {
        nameSuffix = getNameSuffix(domainName)
        logger.debug(`registerName: ${domainName} is a subdomain of ${nameSuffix}`)
        registerUrl = api.subdomains[nameSuffix].registerUrl
      } else {
        logger.debug(`registerName: ${domainName} is not a subdomain`)
      }

      const registrationRequestBody = JSON.stringify({
        name: domainName,
        owner_address: ownerAddress,
        zonefile: zoneFile,
        min_confs: 0,
        unsafe: true
      })

      dispatch(registrationSubmitting())
      logger.trace(`Submitting registration for ${domainName} to Core node at ${registerUrl}`)

      const setOwnerKeyUrl = `http://${api.coreHost}:${api.corePort}/v1/wallet/keys/owner`

      return setOwnerKey(setOwnerKeyUrl, requestHeaders, keypair, nameIsSubdomain)
      .then(() => fetch(registerUrl, {
        method: 'POST',
        headers: requestHeaders,
        body: registrationRequestBody
      })
        .then((response) => response.text())
        .then((responseText) => JSON.parse(responseText))
        .then((responseJson) => {
          if (responseJson.error) {
            logger.error(responseJson.error)
            dispatch(registrationError(responseJson.error))
          } else {
            logger.debug(`Successfully submitted registration for ${domainName}`)
            dispatch(registrationSubmitted())
            const addingUsername = true
            dispatch(IdentityActions.createNewIdentityFromDomain(domainName,
              ownerAddress, addingUsername))
          }
        })
        .catch((error) => {
          logger.error('registerName: error POSTing registration to Core', error)
          dispatch(registrationError(error))
        })
      ).catch((error) => {
        logger.error('registerName: error setting owner key', error)
        dispatch(registrationError(error))
      })
    }).catch((error) => {
      logger.error('registerName: error uploading profile', error)
      dispatch(profileUploadError(error))
    })
  }
}

const RegistrationActions = {
  profileUploading,
  profileUploadError,
  beforeRegister,
  registerName,
  registrationBeforeSubmit,
  registrationSubmitting,
  registrationSubmitted,
  registrationError
}

export default RegistrationActions
<|endoftext|>


<commit_before>import gl from './constants'; // web workers don't have access to GL context, so import all GL constants
import VertexData from './vertex_data';
import hashString from '../utils/hash';

// Describes a vertex layout that can be used with many different GL programs.
export default class VertexLayout {
    // Attribs are an array, in layout order, of: name, size, type, normalized
    // ex: { name: 'position', size: 3, type: gl.FLOAT, normalized: false }
    constructor (attribs) {
        this.attribs = attribs; // array of attributes, specified as standard GL attrib options
        this.dynamic_attribs = this.attribs.filter(x => !x.static); // attributes with per-vertex values, used to build VBOs
        this.components = [];   // list of type and offset info about each attribute component
        this.index = {};        // linear buffer index of each attribute component, e.g. this.index.position.x
        this.stride = 0;        // byte stride of a single vertex

        let index = 0, count = 0;
        for (let a=0; a < this.attribs.length; a++) {
            let attrib = this.attribs[a];
            // Dynamic attribute
            if (attrib.static == null) {
                attrib.offset = this.stride;
                attrib.byte_size = attrib.size;
                let shift = 0;

                switch (attrib.type) {
                    case gl.FLOAT:
                    case gl.INT:
                    case gl.UNSIGNED_INT:
                        attrib.byte_size *= 4;
                        shift = 2;
                        break;
                    case gl.SHORT:
                    case gl.UNSIGNED_SHORT:
                        attrib.byte_size *= 2;
                        shift = 1;
                        break;
                }

                // Force 4-byte alignment on attributes
                if (attrib.byte_size & 3) { // pad to multiple of 4 bytes
                    attrib.byte_size += 4 - (attrib.byte_size & 3);
                }
                this.stride += attrib.byte_size;

                // Add info to list of attribute components (e.g. float is 1 component, vec3 is 3 separate components)
                // Used to map plain JS array to typed arrays
                let offset_typed = attrib.offset >> shift;
                for (let s=0; s < attrib.size; s++) {
                    this.components.push({
                        type: attrib.type,
                        shift,
                        offset: offset_typed++,
                        index: count++
                    });
                }

                // Provide an index into the vertex data buffer for each attribute component
                this.index[attrib.name] = index;
                index += attrib.size;
            }
            // Static attribute
            else {
                attrib.static = Array.isArray(attrib.static) ? attrib.static : [attrib.static]; // convert single value to array
                attrib.method = `vertexAttrib${attrib.static.length}fv`;
            }
        }
    }

    // Setup a vertex layout for a specific GL program
    // Assumes that the desired vertex buffer (VBO) is already bound
    // If a given program doesn't include all attributes, it can still use the vertex layout
    // to read those attribs that it does recognize, using the attrib offsets to skip others.
    enable (gl, program, force) {
        let attrib, location;

        // Enable all attributes for this layout
        for (let a=0; a < this.attribs.length; a++) {
            attrib = this.attribs[a];
            location = program.attribute(attrib.name).location;

            if (location !== -1) {
                // Dynamic attribute
                if (attrib.static == null) {
                    if (!VertexLayout.enabled_attribs[location] || force) {
                        gl.enableVertexAttribArray(location);
                    }
                    gl.vertexAttribPointer(location, attrib.size, attrib.type, attrib.normalized, this.stride, attrib.offset);
                    VertexLayout.enabled_attribs[location] = program;
                }
                // Static attribute
                else {
                    if (gl[attrib.method] instanceof Function) {
                        // N.B.: Safari appears to require an explicit array enable to set vertex attribute as "active"
                        // (the static attribute value method does not work without it). So the attribute is temporarily
                        // enabled as an array, then disabled.
                        gl.enableVertexAttribArray(location);
                        gl[attrib.method](location, attrib.static);
                        gl.disableVertexAttribArray(location);
                    }
                }
            }
        }

        // Disable any previously bound attributes that aren't for this layout
        for (location in VertexLayout.enabled_attribs) {
            this.disableUnusedAttribute(gl, location, program);
        }
    }

    // Disable an attribute if it was not enabled for the specified program
    // NOTE: this was moved out of the inner loop in enable() to assist w/VM optimization
    disableUnusedAttribute (gl, location, program) {
        if (VertexLayout.enabled_attribs[location] !== program) {
            gl.disableVertexAttribArray(location);
            delete VertexLayout.enabled_attribs[location];
        }
    }

    createVertexData () {
        return new VertexData(this);
    }

    // Lazily create the add vertex function
    getAddVertexFunction () {
        if (this.addVertex == null) {
            this.createAddVertexFunction();
        }
        return this.addVertex;
    }

    // Dynamically compile a function to add a plain JS vertex array to this layout's typed VBO arrays
    createAddVertexFunction () {
        let key = hashString(JSON.stringify(this.attribs));
        if (VertexLayout.add_vertex_funcs[key] == null) {
            // `t` = current typed array to write to
            // `o` = current offset into VBO, in current type size (e.g. divide 2 for shorts, divide by 4 for floats, etc.)
            // `v` = plain JS array containing vertex data
            // `vs` = typed arrays (one per GL type needed for this vertex layout)
            // `off` = current offset into VBO, in bytes
            let src = [`var t, o;`];

            // Sort by array type to reduce redundant array look-up and offset calculation
            let last_type;
            let components = [...this.components];
            components.sort((a, b) => (a.type !== b.type) ? (a.type - b.type) : (a.index - b.index));

            for (let c=0; c < components.length; c++) {
                let component = components[c];

                if (last_type !== component.type) {
                    src.push(`t = vs[${component.type}];`);
                    src.push(`o = off${component.shift ? ' >> ' + component.shift : ''};`);
                    last_type = component.type;
                }

                src.push(`t[o + ${component.offset}] = v[${component.index}];`);
            }

            src = src.join('\n');
            let func = new Function('v', 'vs', 'off', src); // jshint ignore:line
            VertexLayout.add_vertex_funcs[key] = func;
        }

        this.addVertex = VertexLayout.add_vertex_funcs[key];
    }

}

// Track currently enabled attribs, by the program they are bound to
// Static class property to reflect global GL state
VertexLayout.enabled_attribs = {};

// Functions to add plain JS vertex array to typed VBO arrays
VertexLayout.add_vertex_funcs = {}; // keyed by unique set of attributes
<commit_msg>fix tracking of enabled vertex attributes when setting static attrib value<commit_after>import gl from './constants'; // web workers don't have access to GL context, so import all GL constants
import VertexData from './vertex_data';
import hashString from '../utils/hash';

// Describes a vertex layout that can be used with many different GL programs.
export default class VertexLayout {
    // Attribs are an array, in layout order, of: name, size, type, normalized
    // ex: { name: 'position', size: 3, type: gl.FLOAT, normalized: false }
    constructor (attribs) {
        this.attribs = attribs; // array of attributes, specified as standard GL attrib options
        this.dynamic_attribs = this.attribs.filter(x => !x.static); // attributes with per-vertex values, used to build VBOs
        this.components = [];   // list of type and offset info about each attribute component
        this.index = {};        // linear buffer index of each attribute component, e.g. this.index.position.x
        this.stride = 0;        // byte stride of a single vertex

        let index = 0, count = 0;
        for (let a=0; a < this.attribs.length; a++) {
            let attrib = this.attribs[a];
            // Dynamic attribute
            if (attrib.static == null) {
                attrib.offset = this.stride;
                attrib.byte_size = attrib.size;
                let shift = 0;

                switch (attrib.type) {
                    case gl.FLOAT:
                    case gl.INT:
                    case gl.UNSIGNED_INT:
                        attrib.byte_size *= 4;
                        shift = 2;
                        break;
                    case gl.SHORT:
                    case gl.UNSIGNED_SHORT:
                        attrib.byte_size *= 2;
                        shift = 1;
                        break;
                }

                // Force 4-byte alignment on attributes
                if (attrib.byte_size & 3) { // pad to multiple of 4 bytes
                    attrib.byte_size += 4 - (attrib.byte_size & 3);
                }
                this.stride += attrib.byte_size;

                // Add info to list of attribute components (e.g. float is 1 component, vec3 is 3 separate components)
                // Used to map plain JS array to typed arrays
                let offset_typed = attrib.offset >> shift;
                for (let s=0; s < attrib.size; s++) {
                    this.components.push({
                        type: attrib.type,
                        shift,
                        offset: offset_typed++,
                        index: count++
                    });
                }

                // Provide an index into the vertex data buffer for each attribute component
                this.index[attrib.name] = index;
                index += attrib.size;
            }
            // Static attribute
            else {
                attrib.static = Array.isArray(attrib.static) ? attrib.static : [attrib.static]; // convert single value to array
                attrib.method = `vertexAttrib${attrib.static.length}fv`;
            }
        }
    }

    // Setup a vertex layout for a specific GL program
    // Assumes that the desired vertex buffer (VBO) is already bound
    // If a given program doesn't include all attributes, it can still use the vertex layout
    // to read those attribs that it does recognize, using the attrib offsets to skip others.
    enable (gl, program, force) {
        let attrib, location;

        // Enable all attributes for this layout
        for (let a=0; a < this.attribs.length; a++) {
            attrib = this.attribs[a];
            location = program.attribute(attrib.name).location;

            if (location !== -1) {
                // Dynamic attribute
                if (attrib.static == null) {
                    if (!VertexLayout.enabled_attribs[location] || force) {
                        gl.enableVertexAttribArray(location);
                    }
                    gl.vertexAttribPointer(location, attrib.size, attrib.type, attrib.normalized, this.stride, attrib.offset);
                    VertexLayout.enabled_attribs[location] = program;
                }
                // Static attribute
                else {
                    if (gl[attrib.method] instanceof Function) {
                        // N.B.: Safari appears to require an explicit array enable to set vertex attribute as "active"
                        // (the static attribute value method does not work without it). So the attribute is temporarily
                        // enabled as an array, then disabled.
                        gl.enableVertexAttribArray(location);
                        gl[attrib.method](location, attrib.static);
                        gl.disableVertexAttribArray(location);
                        delete VertexLayout.enabled_attribs[location];
                    }
                }
            }
        }

        // Disable any previously bound attributes that aren't for this layout
        for (location in VertexLayout.enabled_attribs) {
            this.disableUnusedAttribute(gl, location, program);
        }
    }

    // Disable an attribute if it was not enabled for the specified program
    // NOTE: this was moved out of the inner loop in enable() to assist w/VM optimization
    disableUnusedAttribute (gl, location, program) {
        if (VertexLayout.enabled_attribs[location] !== program) {
            gl.disableVertexAttribArray(location);
            delete VertexLayout.enabled_attribs[location];
        }
    }

    createVertexData () {
        return new VertexData(this);
    }

    // Lazily create the add vertex function
    getAddVertexFunction () {
        if (this.addVertex == null) {
            this.createAddVertexFunction();
        }
        return this.addVertex;
    }

    // Dynamically compile a function to add a plain JS vertex array to this layout's typed VBO arrays
    createAddVertexFunction () {
        let key = hashString(JSON.stringify(this.attribs));
        if (VertexLayout.add_vertex_funcs[key] == null) {
            // `t` = current typed array to write to
            // `o` = current offset into VBO, in current type size (e.g. divide 2 for shorts, divide by 4 for floats, etc.)
            // `v` = plain JS array containing vertex data
            // `vs` = typed arrays (one per GL type needed for this vertex layout)
            // `off` = current offset into VBO, in bytes
            let src = [`var t, o;`];

            // Sort by array type to reduce redundant array look-up and offset calculation
            let last_type;
            let components = [...this.components];
            components.sort((a, b) => (a.type !== b.type) ? (a.type - b.type) : (a.index - b.index));

            for (let c=0; c < components.length; c++) {
                let component = components[c];

                if (last_type !== component.type) {
                    src.push(`t = vs[${component.type}];`);
                    src.push(`o = off${component.shift ? ' >> ' + component.shift : ''};`);
                    last_type = component.type;
                }

                src.push(`t[o + ${component.offset}] = v[${component.index}];`);
            }

            src = src.join('\n');
            let func = new Function('v', 'vs', 'off', src); // jshint ignore:line
            VertexLayout.add_vertex_funcs[key] = func;
        }

        this.addVertex = VertexLayout.add_vertex_funcs[key];
    }

}

// Track currently enabled attribs, by the program they are bound to
// Static class property to reflect global GL state
VertexLayout.enabled_attribs = {};

// Functions to add plain JS vertex array to typed VBO arrays
VertexLayout.add_vertex_funcs = {}; // keyed by unique set of attributes
<|endoftext|>


<commit_before><commit_msg>Fixed cli to newsletter count tool<commit_after><|endoftext|>