<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'
import { Link } from 'react-router-dom'
import { Tooltip, Icon, Button, Spin, Modal, message } from 'antd'
import { fetchInterfaceColList, fetchCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';
import { handleMockWord, simpleJsonPathParse } from '../../../../common.js'
// import { formatTime } from '../../../../common.js'
import * as Table from 'reactabular-table';
import * as dnd from 'reactabular-dnd';
import * as resolve from 'table-resolver';
import axios from 'axios'
import URL from 'url';
import Mock from 'mockjs'
import json5 from 'json5'
import CaseReport from './CaseReport.js'
const MockExtra = require('common/mock-extra.js')

function json_parse(data) {
  try {
    return json5.parse(data)
  } catch (e) {
    return data
  }
}

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol,
      currCaseList: state.interfaceCol.currCaseList,
      currProject: state.project.currProject
    }
  },
  {
    fetchInterfaceColList,
    fetchCaseList,
    setColData
  }
)
@withRouter
@DragDropContext(HTML5Backend)
class InterfaceColContent extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currCaseList: PropTypes.array,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool,
    currProject: PropTypes.object
  }

  constructor(props) {
    super(props);
    this.reports = {};
    this.records = {};
    this.state = {
      rows: [],
      reports: {},
      visible: false,
      curCaseid: null,
      hasPlugin: false
    };
    this.onRow = this.onRow.bind(this);
    this.onMoveRow = this.onMoveRow.bind(this);
  }

  async componentWillMount() {
    const result = await this.props.fetchInterfaceColList(this.props.match.params.id)
    let { currColId } = this.props;
    const params = this.props.match.params;
    const { actionId } = params;
    currColId = +actionId ||
      result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
      result.payload.data.data[0]._id;
    this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
    if (currColId && currColId != 0) {
      await this.props.fetchCaseList(currColId);
      this.props.setColData({ currColId: +currColId, isShowCol: true })
      this.handleColdata(this.props.currCaseList)
    }

    let startTime = 0;
    this.interval = setInterval(() => {
      startTime += 500;
      if (startTime > 5000) {
        clearInterval(this.interval);
      }
      if (window.crossRequest) {
        clearInterval(this.interval);
        this.setState({
          hasPlugin: true
        })
      } else {
        this.setState({
          hasPlugin: false
        })
      }
    }, 500)
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  handleColdata = (rows) => {
    rows = rows.map((item) => {
      item.id = item._id;
      item._test_status = item.test_status;
      return item;
    })
    rows = rows.sort((n, o) => {
      return n.index > o.index
    })
    this.setState({
      rows: rows
    })
  }

  executeTests = async () => {
    for (let i = 0, l = this.state.rows.length, newRows, curitem; i < l; i++) {
      let { rows } = this.state;
      curitem = Object.assign({}, rows[i], { test_status: 'loading' });
      newRows = [].concat([], rows);
      newRows[i] = curitem;
      this.setState({
        rows: newRows
      })
      let status = 'error', result;
      try {
        result = await this.handleTest(curitem);
        if (result.code === 400) {
          status = 'error';
        } else if (result.code === 0) {
          status = 'ok';
        } else if (result.code === 1) {
          status = 'invalid'
        }
      } catch (e) {
        console.error(e);
        status = 'error';
        result = e;
      }
      this.reports[curitem._id] = result;
      this.records[curitem._id] = result.res_body;

      curitem = Object.assign({}, rows[i], { test_status: status });
      newRows = [].concat([], rows);
      newRows[i] = curitem;
      this.setState({
        rows: newRows
      })
    }
  }

  handleTest = (interfaceData) => {
    const { currProject } = this.props;
    const { case_env } = interfaceData;
    let path = URL.resolve(currProject.basepath, interfaceData.path);
    interfaceData.req_params = interfaceData.req_params || [];
    interfaceData.req_params.forEach(item => {
      path = path.replace(`:${item.name}`, item.value || `:${item.name}`);
    });
    const domains = currProject.env.concat();
    let currDomain = domains.find(item => item.name === case_env);
    if(!currDomain){
      currDomain = domains[0];
    }
    const urlObj = URL.parse(currDomain.domain);
    if(urlObj.pathname){
      if(urlObj.pathname[urlObj.pathname.length - 1] !== '/'){
        urlObj.pathname += '/'
      }
    }

    const href = URL.format({
      protocol: urlObj.protocol || 'http',
      host: urlObj.host,
      pathname: urlObj.pathname ? URL.resolve(urlObj.pathname, '.' + path) : path,
      query: this.getQueryObj(interfaceData.req_query)
    });

    return new Promise((resolve, reject) => {
      let result = { code: 400, msg: '数据异常', validRes: [] };
      let that = this;

      result.url = href;
      result.method = interfaceData.method;
      result.headers = that.getHeadersObj(interfaceData.req_headers);
      result.body = interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other;

      window.crossRequest({
        url: href,
        method: interfaceData.method,
        headers: that.getHeadersObj(interfaceData.req_headers),
        data: interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other,
        success: (res, header) => {
          res = json_parse(res);
          result.res_header = header;
          result.res_body = res;
          if (res && typeof res === 'object') {
            let tpl = MockExtra(json_parse(interfaceData.res_body), {
              query: interfaceData.req_query,
              body: interfaceData.req_body_form
            })

            let validRes = [];
            if (interfaceData.mock_verify) {
              validRes = Mock.valid(tpl, res);
            }
            if (validRes.length === 0) {
              result.code = 0;
              result.validRes = [{ message: '验证通过' }];
              resolve(result);
            } else if (validRes.length > 0) {
              result.code = 1;
              result.validRes = validRes;
              resolve(result)
            }
          } else {
            reject(result)
          }
        },
        error: (err, header) => {
          try {
            err = json_parse(err);
          } catch (e) {
            console.log(e)
          }

          err = err || '请求异常';
          result.code = 400;
          result.res_header = header;
          result.res_body = err;
          reject(result)
        }
      })
    })
  }

  handleVarWord(val) {
    return simpleJsonPathParse(val, this.records)
  }

  handleValue(val) {
    if (!val || typeof val !== 'string') {
      return val;
    } else if (val[0] === '@') {
      return handleMockWord(val);
    } else if (val.indexOf('$.') === 0) {
      return this.handleVarWord(val);
    }
    return val;
  }

  arrToObj = (arr) => {
    arr = arr || [];
    const obj = {};
    arr.forEach(item => {
      if (item.name && item.type !== 'file') {
        obj[item.name] = this.handleValue(item.value);
      }
    })
    return obj;
  }

  getQueryObj = (query) => {
    query = query || [];
    const queryObj = {};
    query.forEach(item => {
      if (item.name) {
        queryObj[item.name] = this.handleValue(item.value);
      }
    })
    return queryObj;
  }
  getHeadersObj = (headers) => {
    headers = headers || [];
    const headersObj = {};
    headers.forEach(item => {
      if (item.name && item.value) {
        headersObj[item.name] = item.value;
      }
    })
    return headersObj;
  }

  onRow(row) {
    return {
      rowId: row.id,
      onMove: this.onMoveRow
    };
  }
  onMoveRow({ sourceRowId, targetRowId }) {
    let rows = dnd.moveRows({
      sourceRowId,
      targetRowId
    })(this.state.rows);
    let changes = [];
    rows.forEach((item, index) => {
      changes.push({
        id: item._id,
        index: index
      })
    })

    axios.post('/api/col/up_col_index', changes).then()
    if (rows) {
      this.setState({ rows });
    }
  }

  async componentWillReceiveProps(nextProps) {
    const { interfaceColList } = nextProps;
    const { actionId: oldColId, id } = this.props.match.params
    let newColId = nextProps.match.params.actionId
    if (!interfaceColList.find(item => +item._id === +newColId)) {
      this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
    } else if ((oldColId !== newColId) || interfaceColList !== this.props.interfaceColList) {
      if (newColId && newColId != 0) {
        await this.props.fetchCaseList(newColId);
        this.props.setColData({ currColId: +newColId, isShowCol: true })
        this.handleColdata(this.props.currCaseList)
      }
    }
  }

  openReport = (id) => {
    if (!this.reports[id]) {
      return message.warn('还没有生成报告')
    }
    this.setState({
      visible: true,
      curCaseid: id
    })

  }

  handleCancel = () => {
    this.setState({
      visible: false
    });
  }

  render() {
    const columns = [{
      property: 'casename',
      header: {
        label: '用例名称'
      },
      props: {
        style: {
          width: '250px'
        }
      },
      cell: {
        formatters: [
          (text, { rowData }) => {
            let record = rowData;
            return <Link to={"/project/" + record.project_id + "/interface/case/" + record._id}>{record.casename}</Link>
          }
        ]
      }
    }, {
      header: {
        label: 'key',
        formatters: [() => {
          return <Tooltip title="每个用例都有一个独一无二的key，可用来获取匹配的接口响应数据">
            Key</Tooltip>
        }]
      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [
          (value, { rowData }) => {
            return {rowData._id}
          }]
      }
    }, {
      property: 'test_status',
      header: {
        label: '状态'
      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [(value, { rowData }) => {
          switch (rowData.test_status) {
            case 'ok':
              return
<Icon style={{ color: '#00a854' }} type="check-circle" />

            case 'error':
              return
<Tooltip title="请求异常"><Icon type="info-circle" style={{ color: '#f04134' }} /></Tooltip>

            case 'invalid':
              return
<Tooltip title="返回数据校验未通过"><Icon type="exclamation-circle" style={{ color: '#ffbf00' }} /></Tooltip>

            case 'loading':
              return
<Spin />

            default:
              return
<Icon style={{ color: '#00a854' }} type="check-circle" />

          }
        }]
      }
    }, {
      property: 'path',
      header: {
        label: '接口路径'
      },
      cell: {
        formatters: [
          (text, { rowData }) => {
            let record = rowData;
            return (
              <Tooltip title="跳转到对应接口">
                <Link to={`/project/${record.project_id}/interface/api/${record.interface_id}`}>{record.path}</Link>
              </Tooltip>
            )
          }
        ]
      }
    }, {
      header: {
        label: '测试报告'

      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [(text, { rowData }) => {
          if (!this.reports[rowData.id]) {
            return null;
          }
          return <Button onClick={() => this.openReport(rowData.id)}>报告</Button>
        }]
      }
    }
    ];
    const { rows } = this.state;
    const components = {
      header: {
        cell: dnd.Header
      },
      body: {
        row: dnd.Row
      }
    };

    const resolvedColumns = resolve.columnChildren({ columns });
    const resolvedRows = resolve.resolve({
      columns: resolvedColumns,
      method: resolve.nested
    })(rows);

    return (

测试集合
          <Tooltip title="点击查看文档"><Icon type="question-circle-o" /></Tooltip>

        {this.state.hasPlugin?
          <Button type="primary" style={{ float: 'right' }} onClick={this.executeTests}>开始测试</Button>:
          <Tooltip title="请安装 cross-request Chrome 插件">
            <Button disabled type="primary" style={{ float: 'right' }} >开始测试</Button>
          </Tooltip>
        }

        <Modal
          title="测试报告"
          width="660px"
          style={{ minHeight: '500px' }}
          visible={this.state.visible}
          onCancel={this.handleCancel}
          footer={null}
        >
          <CaseReport {...this.reports[this.state.curCaseid]} />
        </Modal>

    )
  }
}

export default InterfaceColContent
<commit_msg>feat: 添加测试集选择执行环境的功能<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'
import { Link } from 'react-router-dom'
import { Tooltip, Icon, Button, Spin, Modal, message ,Select} from 'antd'
import { fetchInterfaceColList, fetchCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';
import { handleMockWord, simpleJsonPathParse } from '../../../../common.js'
// import { formatTime } from '../../../../common.js'
import * as Table from 'reactabular-table';
import * as dnd from 'reactabular-dnd';
import * as resolve from 'table-resolver';
import axios from 'axios'
import URL from 'url';
import Mock from 'mockjs'
import json5 from 'json5'
import CaseReport from './CaseReport.js'
const MockExtra = require('common/mock-extra.js')
const Option = Select.Option;
function json_parse(data) {
  try {
    return json5.parse(data)
  } catch (e) {
    return data
  }
}

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol,
      currCaseList: state.interfaceCol.currCaseList,
      currProject: state.project.currProject
    }
  },
  {
    fetchInterfaceColList,
    fetchCaseList,
    setColData
  }
)
@withRouter
@DragDropContext(HTML5Backend)
class InterfaceColContent extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currCaseList: PropTypes.array,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool,
    currProject: PropTypes.object
  }

  constructor(props) {
    super(props);
    this.reports = {};
    this.records = {};
    this.state = {
      rows: [],
      reports: {},
      visible: false,
      curCaseid: null,
      hasPlugin: false
    };
    this.onRow = this.onRow.bind(this);
    this.onMoveRow = this.onMoveRow.bind(this);
  }

  async componentWillMount() {
    const result = await this.props.fetchInterfaceColList(this.props.match.params.id)
    let { currColId } = this.props;
    const params = this.props.match.params;
    const { actionId } = params;
    currColId = +actionId ||
      result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
      result.payload.data.data[0]._id;
    this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
    if (currColId && currColId != 0) {
      await this.props.fetchCaseList(currColId);
      this.props.setColData({ currColId: +currColId, isShowCol: true })
      this.handleColdata(this.props.currCaseList)
    }

    let startTime = 0;
    this.interval = setInterval(() => {
      startTime += 500;
      if (startTime > 5000) {
        clearInterval(this.interval);
      }
      if (window.crossRequest) {
        clearInterval(this.interval);
        this.setState({
          hasPlugin: true
        })
      } else {
        this.setState({
          hasPlugin: false
        })
      }
    }, 500)
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  handleColdata = (rows) => {
    rows = rows.map((item) => {
      item.id = item._id;
      item._test_status = item.test_status;
      return item;
    })
    rows = rows.sort((n, o) => {
      return n.index > o.index
    })
    this.setState({
      rows: rows
    })
  }

  executeTests = async () => {
    for (let i = 0, l = this.state.rows.length, newRows, curitem; i < l; i++) {
      let { rows } = this.state;
      curitem = Object.assign({}, rows[i], { test_status: 'loading' });
      newRows = [].concat([], rows);
      newRows[i] = curitem;
      this.setState({
        rows: newRows
      })
      let status = 'error', result;
      try {
        result = await this.handleTest(curitem);
        if (result.code === 400) {
          status = 'error';
        } else if (result.code === 0) {
          status = 'ok';
        } else if (result.code === 1) {
          status = 'invalid'
        }
      } catch (e) {
        console.error(e);
        status = 'error';
        result = e;
      }
      this.reports[curitem._id] = result;
      this.records[curitem._id] = result.res_body;

      curitem = Object.assign({}, rows[i], { test_status: status });
      newRows = [].concat([], rows);
      newRows[i] = curitem;
      this.setState({
        rows: newRows
      })
    }
  }

  handleTest = (interfaceData) => {
    const { currProject } = this.props;
    const { case_env } = interfaceData;
    let path = URL.resolve(currProject.basepath, interfaceData.path);
    interfaceData.req_params = interfaceData.req_params || [];
    interfaceData.req_params.forEach(item => {
      path = path.replace(`:${item.name}`, item.value || `:${item.name}`);
    });
    const domains = currProject.env.concat();
    let currDomain = domains.find(item => item.name === case_env);
    if(!currDomain){
      currDomain = domains[0];
    }
    const urlObj = URL.parse(currDomain.domain);
    if(urlObj.pathname){
      if(urlObj.pathname[urlObj.pathname.length - 1] !== '/'){
        urlObj.pathname += '/'
      }
    }

    const href = URL.format({
      protocol: urlObj.protocol || 'http',
      host: urlObj.host,
      pathname: urlObj.pathname ? URL.resolve(urlObj.pathname, '.' + path) : path,
      query: this.getQueryObj(interfaceData.req_query)
    });

    return new Promise((resolve, reject) => {
      let result = { code: 400, msg: '数据异常', validRes: [] };
      let that = this;

      result.url = href;
      result.method = interfaceData.method;
      result.headers = that.getHeadersObj(interfaceData.req_headers);
      result.body = interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other;

      window.crossRequest({
        url: href,
        method: interfaceData.method,
        headers: that.getHeadersObj(interfaceData.req_headers),
        data: interfaceData.req_body_type === 'form' ? that.arrToObj(interfaceData.req_body_form) : interfaceData.req_body_other,
        success: (res, header) => {
          res = json_parse(res);
          result.res_header = header;
          result.res_body = res;
          if (res && typeof res === 'object') {
            let tpl = MockExtra(json_parse(interfaceData.res_body), {
              query: interfaceData.req_query,
              body: interfaceData.req_body_form
            })

            let validRes = [];
            if (interfaceData.mock_verify) {
              validRes = Mock.valid(tpl, res);
            }
            if (validRes.length === 0) {
              result.code = 0;
              result.validRes = [{ message: '验证通过' }];
              resolve(result);
            } else if (validRes.length > 0) {
              result.code = 1;
              result.validRes = validRes;
              resolve(result)
            }
          } else {
            reject(result)
          }
        },
        error: (err, header) => {
          try {
            err = json_parse(err);
          } catch (e) {
            console.log(e)
          }

          err = err || '请求异常';
          result.code = 400;
          result.res_header = header;
          result.res_body = err;
          reject(result)
        }
      })
    })
  }

  handleVarWord(val) {
    return simpleJsonPathParse(val, this.records)
  }

  handleValue(val) {
    if (!val || typeof val !== 'string') {
      return val;
    } else if (val[0] === '@') {
      return handleMockWord(val);
    } else if (val.indexOf('$.') === 0) {
      return this.handleVarWord(val);
    }
    return val;
  }

  arrToObj = (arr) => {
    arr = arr || [];
    const obj = {};
    arr.forEach(item => {
      if (item.name && item.type !== 'file') {
        obj[item.name] = this.handleValue(item.value);
      }
    })
    return obj;
  }

  getQueryObj = (query) => {
    query = query || [];
    const queryObj = {};
    query.forEach(item => {
      if (item.name) {
        queryObj[item.name] = this.handleValue(item.value);
      }
    })
    return queryObj;
  }
  getHeadersObj = (headers) => {
    headers = headers || [];
    const headersObj = {};
    headers.forEach(item => {
      if (item.name && item.value) {
        headersObj[item.name] = item.value;
      }
    })
    return headersObj;
  }

  onRow(row) {
    return {
      rowId: row.id,
      onMove: this.onMoveRow
    };
  }
  onMoveRow({ sourceRowId, targetRowId }) {
    let rows = dnd.moveRows({
      sourceRowId,
      targetRowId
    })(this.state.rows);
    let changes = [];
    rows.forEach((item, index) => {
      changes.push({
        id: item._id,
        index: index
      })
    })

    axios.post('/api/col/up_col_index', changes).then()
    if (rows) {
      this.setState({ rows });
    }
  }

  async componentWillReceiveProps(nextProps) {
    const { interfaceColList } = nextProps;
    const { actionId: oldColId, id } = this.props.match.params
    let newColId = nextProps.match.params.actionId
    if (!interfaceColList.find(item => +item._id === +newColId)) {
      this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
    } else if ((oldColId !== newColId) || interfaceColList !== this.props.interfaceColList) {
      if (newColId && newColId != 0) {
        await this.props.fetchCaseList(newColId);
        this.props.setColData({ currColId: +newColId, isShowCol: true })
        this.handleColdata(this.props.currCaseList)
      }
    }
  }

  openReport = (id) => {
    if (!this.reports[id]) {
      return message.warn('还没有生成报告')
    }
    this.setState({
      visible: true,
      curCaseid: id
    })

  }

  handleCancel = () => {
    this.setState({
      visible: false
    });
  }

  colEnvChange = (envName) => {
    let rows = [...this.state.rows];
    for(var i in rows){
      rows[i].case_env = envName;
    }
    this.setState({
      rows: [...rows]
    });
  }

  render() {
    const columns = [{
      property: 'casename',
      header: {
        label: '用例名称'
      },
      props: {
        style: {
          width: '250px'
        }
      },
      cell: {
        formatters: [
          (text, { rowData }) => {
            let record = rowData;
            return <Link to={"/project/" + record.project_id + "/interface/case/" + record._id}>{record.casename}</Link>
          }
        ]
      }
    }, {
      header: {
        label: 'key',
        formatters: [() => {
          return <Tooltip title="每个用例都有一个独一无二的key，可用来获取匹配的接口响应数据">
            Key</Tooltip>
        }]
      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [
          (value, { rowData }) => {
            return {rowData._id}
          }]
      }
    }, {
      property: 'test_status',
      header: {
        label: '状态'
      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [(value, { rowData }) => {
          switch (rowData.test_status) {
            case 'ok':
              return
<Icon style={{ color: '#00a854' }} type="check-circle" />

            case 'error':
              return
<Tooltip title="请求异常"><Icon type="info-circle" style={{ color: '#f04134' }} /></Tooltip>

            case 'invalid':
              return
<Tooltip title="返回数据校验未通过"><Icon type="exclamation-circle" style={{ color: '#ffbf00' }} /></Tooltip>

            case 'loading':
              return
<Spin />

            default:
              return
<Icon style={{ color: '#00a854' }} type="check-circle" />

          }
        }]
      }
    }, {
      property: 'path',
      header: {
        label: '接口路径'
      },
      cell: {
        formatters: [
          (text, { rowData }) => {
            let record = rowData;
            return (
              <Tooltip title="跳转到对应接口">
                <Link to={`/project/${record.project_id}/interface/api/${record.interface_id}`}>{record.path}</Link>
              </Tooltip>
            )
          }
        ]
      }
    }, {
      header: {
        label: '测试报告'

      },
      props: {
        style: {
          width: '100px'
        }
      },
      cell: {
        formatters: [(text, { rowData }) => {
          if (!this.reports[rowData.id]) {
            return null;
          }
          return <Button onClick={() => this.openReport(rowData.id)}>报告</Button>
        }]
      }
    }
    ];
    const { rows } = this.state;
    const components = {
      header: {
        cell: dnd.Header
      },
      body: {
        row: dnd.Row
      }
    };

    const resolvedColumns = resolve.columnChildren({ columns });
    const resolvedRows = resolve.resolve({
      columns: resolvedColumns,
      method: resolve.nested
    })(rows);
    let colEnv = this.props.currProject.env || [];
    return (

测试集合
          <Tooltip title="点击查看文档"><Icon type="question-circle-o" /></Tooltip>

          <Select defaultValue={"选择测试集合执行环境（不选即为默认环境）"} style={{ width: "300px" }} onChange={this.colEnvChange}>
            {
              colEnv.map((item)=>{
                return <Option key={item._id} value={item.name}>{item.name+": "+item.domain}</Option>;
              })
            }
          </Select>

        {this.state.hasPlugin?
          <Button type="primary" style={{ float: 'right' }} onClick={this.executeTests}>开始测试</Button>:
          <Tooltip title="请安装 cross-request Chrome 插件">
            <Button disabled type="primary" style={{ float: 'right' }} >开始测试</Button>
          </Tooltip>
        }

        <Modal
          title="测试报告"
          width="660px"
          style={{ minHeight: '500px' }}
          visible={this.state.visible}
          onCancel={this.handleCancel}
          footer={null}
        >
          <CaseReport {...this.reports[this.state.curCaseid]} />
        </Modal>

    )
  }
}

export default InterfaceColContent
<|endoftext|>


<commit_before>// DOM elements
var $source;
var $photographer;
var $save;
var $textColor;
var $logo;
var $crop;
var $logoColor;
var $imageLoader;
var $canvas;
var canvas;
var $qualityQuestions;
var $copyrightHolder;
var $dragHelp;
var $filename;
var $customFilename;

// state
var scaledImageHeight;
var fixedWidth = 1000;
var dy = 0;
var logoDimensions = {
    'npr': {
        w: 148,
        h: 49
    },
    'music': {
        w: 305,
        h: 80
    }
};
var elementPadding = 40;
var image;
var imageFilename = 'image';
var currentCrop = 'twitter';
var currentLogo = 'npr';
var currentLogoColor = 'white';
var currentTextColor = 'white';
var currentCopyright;
var credit = 'Belal Khan/Flickr'

// JS objects
var ctx;
var img = new Image();
var logo = new Image();

var onDocumentLoad = function(e) {
    $source = $('#source');
    $photographer = $('#photographer');
    $canvas = $('#imageCanvas');
    canvas = $canvas[0];
    $imageLoader = $('#imageLoader');
    ctx = canvas.getContext('2d');
    $save = $('.save-btn');
    $textColor = $('input[name="textColor"]');
    $logo = $('input[name="logo"]');
    $crop = $('input[name="crop"]');
    $logoColor = $('input[name="logoColor"]');
    $qualityQuestions = $('.quality-question');
    $copyrightHolder = $('.copyright-holder');
    $dragHelp = $('.drag-help');
    $filename = $('.fileinput-filename');
    $customFilename = $('.custom-filename');

    img.src = APP_CONFIG.DEFAULT_IMAGE;
    img.onload = renderCanvas;
    logo.src = 'assets/logo-' + currentLogo + '-' + currentLogoColor + '.png';
    logo.onload = renderCanvas;

    $photographer.on('keyup', renderCanvas);
    $source.on('keyup', renderCanvas);
    $imageLoader.on('change', handleImage);
    $save.on('click', onSaveClick);
    $textColor.on('change', onTextColorChange);
    $logo.on('change', onLogoChange);
    $logoColor.on('change', onLogoColorChange);
    $crop.on('change', onCropChange);
    $canvas.on('mousedown', onDrag);
    $copyrightHolder.on('change', onCopyrightChange);
    $customFilename.on('click', function(e) {
        e.stopPropagation();
    })

    $("body").on("contextmenu", "canvas", function(e) {
        return false;
    });

    renderCanvas();
}

/*
* Draw the image, then the logo, then the text
*/
var renderCanvas = function() {
    // canvas is always the same width
    canvas.width = fixedWidth;

    // if we're cropping, use the aspect ratio for the height
    if (currentCrop !== 'original') {
        canvas.height = fixedWidth / (16/9);
    }

    // clear the canvas
    ctx.clearRect(0,0,canvas.width,canvas.height);

    // determine height of canvas and scaled image, then draw the image
    var imageAspect = img.width / img.height;
    if (currentCrop === 'original') {
        canvas.height = fixedWidth / imageAspect;
        scaledImageHeight = canvas.height;
        ctx.drawImage(
            img,
            0,
            0,
            fixedWidth,
            scaledImageHeight
        );
    } else {
        scaledImageHeight = fixedWidth / imageAspect;
        ctx.drawImage(
            img,
            0,
            0,
            img.width,
            img.height,
            0,
            dy,
            fixedWidth,
            scaledImageHeight
        );
    }

    // set alpha channel, draw the logo
    if (currentLogoColor === 'white') {
        ctx.globalAlpha = "0.8";
    } else {
        ctx.globalAlpha = "0.6";
    }
    ctx.drawImage(
        logo,
        elementPadding,
        currentLogo === 'npr'? elementPadding : elementPadding - 15,
        logoDimensions[currentLogo]['w'],
        logoDimensions[currentLogo]['h']
    );

    // reset alpha channel so text is not translucent
    ctx.globalAlpha = "1";

    // draw the text
    ctx.textBaseline = 'bottom';
    ctx.textAlign = 'left';
    ctx.fillStyle = currentTextColor;
    ctx.font = 'normal 20pt "Gotham SSm"';

    if (currentTextColor === 'white') {
        ctx.shadowColor = 'rgba(0,0,0,0.7)';
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
        ctx.shadowBlur = 10;
    }

    if (currentCopyright) {
        credit = buildCreditString();
    }

    var creditWidth = ctx.measureText(credit);
    ctx.fillText(
        credit,
        canvas.width - (creditWidth.width + elementPadding),
        canvas.height - elementPadding
    );

    validateForm();
}

/*
* Build the proper format for the credit based on current copyright
*/
var buildCreditString = function() {
    var creditString;
    var val = $copyrightHolder.val();

    if (val === 'npr') {
        if ($photographer.val() === '') {
            creditString = 'NPR';
        } else {
            creditString = $photographer.val() + '/NPR';
        }
    } else if (val === 'freelance') {
        creditString = $photographer.val() + ' for NPR';
        if ($photographer.val() !== '') {
            $photographer.parents('.form-group').removeClass('has-warning');
        } else {
            $photographer.parents('.form-group').addClass('has-warning');
        }
    } else if (val === 'ap') {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/AP';
        } else {
            creditString = 'AP';
        }
    } else if (val === 'getty') {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/Getty';
        } else {
            creditString = 'Getty';
        }
    } else {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/' + $source.val();
        } else {
            creditString = $source.val();
        }

        if ($source.val() !== '') {
            $source.parents('.form-group').removeClass('has-warning');
        } else {
            $source.parents('.form-group').addClass('has-warning');
        }
    }

    return creditString;
}

/*
* Check to see if any required fields have not been
* filled out before enabling saving
*/
var validateForm = function() {
    if ($('.has-warning').length === 0 && currentCopyright) {
        $save.removeAttr('disabled');
        $("body").off("contextmenu", "canvas");
    } else {
        $save.attr('disabled', '');
        $("body").on("contextmenu", "canvas", function(e) {
            return false;
        });
    }
}

/*
* Handle dragging the image for crops when applicable
*/
var onDrag = function(e) {
    e.preventDefault();
    var originY = e.clientY;
    var startY = dy;

    if (currentCrop === 'original') {
        return;
    }

    function update(e) {
        if (Math.abs(e.clientY - originY) > 1) {
            dy = startY - (originY - e.clientY);

            // Prevent dragging image below upper bound
            if (dy > 0) {
                dy = 0;
                return;
            }

            // Prevent dragging image above lower bound
            if (dy < canvas.height - scaledImageHeight) {
                dy = canvas.height - scaledImageHeight;
                return;
            }

            renderCanvas();
        }
    }

    // Perform drag sequence:
    $(document).on('mousemove.drag', _.debounce(update, 5, true))
        .on('mouseup.drag', function(e) {
            $(document).off('mouseup.drag mousemove.drag');
            update(e);
        });
}

/*
* Take an image from file input and load it
*/
var handleImage = function(e) {
    var reader = new FileReader();
    reader.onload = function(e){
        image = e.target.result;
        imageFilename = $('.fileinput-filename').text().split('.')[0];
        img.src = image;
        $customFilename.text(imageFilename);
    }
    reader.readAsDataURL(e.target.files[0]);
}

/*
* Load the logo based on radio buttons
*/
var loadLogo = function() {
    logo.src = 'assets/logo-' + currentLogo + '-' + currentLogoColor + '.png';
}

/*
* Download the image on save click
*/
var onSaveClick = function(e) {
    e.preventDefault();

    /// create an "off-screen" anchor tag
    var link = document.createElement('a'),
        e;

    /// the key here is to set the download attribute of the a tag
    if ($customFilename.text()) {
        imageFilename = $customFilename.text();
    }
    link.download =  'twitterbug-' + imageFilename + '.png';

    /// convert canvas content to data-uri for link. When download
    /// attribute is set the content pointed to by link will be
    /// pushed as "download" in HTML5 capable browsers
    link.href = canvas.toDataURL();
    link.target = "_blank";

    /// create a "fake" click-event to trigger the download
    if (document.createEvent) {

        e = document.createEvent("MouseEvents");
        e.initMouseEvent("click", true, true, window,
                         0, 0, 0, 0, 0, false, false, false,
                         false, 0, null);

        link.dispatchEvent(e);

    } else if (link.fireEvent) {
        link.fireEvent("onclick");
    }
}

/*
* Handle logo radio button clicks
*/
var onLogoColorChange = function(e) {
    currentLogoColor = $(this).val();

    loadLogo();
    renderCanvas();
}

/*
* Handle text color radio button clicks
*/
var onTextColorChange = function(e) {
    currentTextColor = $(this).val();

    renderCanvas();
}

/*
* Handle logo radio button clicks
*/
var onLogoChange = function(e) {
    currentLogo = $(this).val();

    loadLogo();
    renderCanvas();
}

/*
* Handle crop radio button clicks
*/
var onCropChange = function() {
    currentCrop = $(this).val();

    if (currentCrop !== 'original') {
        $canvas.addClass('is-draggable');
        $dragHelp.show();
    } else {
        $canvas.removeClass('is-draggable');
        $dragHelp.hide();
    }

    renderCanvas();
}

/*
* Show the appropriate fields based on the chosen copyright
*/
var onCopyrightChange = function() {
    currentCopyright = $copyrightHolder.val();
    $photographer.parents('.form-group').removeClass('has-warning');
    $source.parents('.form-group').removeClass('has-warning');

    if (currentCopyright === 'npr') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group').slideUp();
    } else if (currentCopyright === 'freelance') {
        $photographer.parents('.form-group').slideDown();
        $source.parents('.form-group').slideUp();
        $photographer.parents('.form-group').addClass('has-warning required');
    } else if (currentCopyright === 'ap' || currentCopyright === 'getty') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group')
            .slideUp()
            .removeClass('has-warning required');

    } else if (currentCopyright === 'third-party') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group').slideDown();
        $source.parents('.form-group').addClass('has-warning required');
    } else {
        credit = '';
        $photographer.parents('.form-group').slideUp();
        $source.parents('.form-group')
            .slideUp()
            .parents('.form-group').removeClass('has-warning required');
    }

    renderCanvas();
}

$(onDocumentLoad);<commit_msg>fix logo dimensions<commit_after>// DOM elements
var $source;
var $photographer;
var $save;
var $textColor;
var $logo;
var $crop;
var $logoColor;
var $imageLoader;
var $canvas;
var canvas;
var $qualityQuestions;
var $copyrightHolder;
var $dragHelp;
var $filename;
var $customFilename;

// state
var scaledImageHeight;
var fixedWidth = 1000;
var dy = 0;
var logoDimensions = {
    'npr': {
        w: 150,
        h: 51
    },
    'music': {
        w: 306,
        h: 81
    }
};
var elementPadding = 40;
var image;
var imageFilename = 'image';
var currentCrop = 'twitter';
var currentLogo = 'npr';
var currentLogoColor = 'white';
var currentTextColor = 'white';
var currentCopyright;
var credit = 'Belal Khan/Flickr'

// JS objects
var ctx;
var img = new Image();
var logo = new Image();

var onDocumentLoad = function(e) {
    $source = $('#source');
    $photographer = $('#photographer');
    $canvas = $('#imageCanvas');
    canvas = $canvas[0];
    $imageLoader = $('#imageLoader');
    ctx = canvas.getContext('2d');
    $save = $('.save-btn');
    $textColor = $('input[name="textColor"]');
    $logo = $('input[name="logo"]');
    $crop = $('input[name="crop"]');
    $logoColor = $('input[name="logoColor"]');
    $qualityQuestions = $('.quality-question');
    $copyrightHolder = $('.copyright-holder');
    $dragHelp = $('.drag-help');
    $filename = $('.fileinput-filename');
    $customFilename = $('.custom-filename');

    img.src = APP_CONFIG.DEFAULT_IMAGE;
    img.onload = renderCanvas;
    logo.src = 'assets/logo-' + currentLogo + '-' + currentLogoColor + '.png';
    logo.onload = renderCanvas;

    $photographer.on('keyup', renderCanvas);
    $source.on('keyup', renderCanvas);
    $imageLoader.on('change', handleImage);
    $save.on('click', onSaveClick);
    $textColor.on('change', onTextColorChange);
    $logo.on('change', onLogoChange);
    $logoColor.on('change', onLogoColorChange);
    $crop.on('change', onCropChange);
    $canvas.on('mousedown', onDrag);
    $copyrightHolder.on('change', onCopyrightChange);
    $customFilename.on('click', function(e) {
        e.stopPropagation();
    })

    $("body").on("contextmenu", "canvas", function(e) {
        return false;
    });

    renderCanvas();
}

/*
* Draw the image, then the logo, then the text
*/
var renderCanvas = function() {
    // canvas is always the same width
    canvas.width = fixedWidth;

    // if we're cropping, use the aspect ratio for the height
    if (currentCrop !== 'original') {
        canvas.height = fixedWidth / (16/9);
    }

    // clear the canvas
    ctx.clearRect(0,0,canvas.width,canvas.height);

    // determine height of canvas and scaled image, then draw the image
    var imageAspect = img.width / img.height;
    if (currentCrop === 'original') {
        canvas.height = fixedWidth / imageAspect;
        scaledImageHeight = canvas.height;
        ctx.drawImage(
            img,
            0,
            0,
            fixedWidth,
            scaledImageHeight
        );
    } else {
        scaledImageHeight = fixedWidth / imageAspect;
        ctx.drawImage(
            img,
            0,
            0,
            img.width,
            img.height,
            0,
            dy,
            fixedWidth,
            scaledImageHeight
        );
    }

    // set alpha channel, draw the logo
    if (currentLogoColor === 'white') {
        ctx.globalAlpha = "0.8";
    } else {
        ctx.globalAlpha = "0.6";
    }
    ctx.drawImage(
        logo,
        elementPadding,
        currentLogo === 'npr'? elementPadding : elementPadding - 14,
        logoDimensions[currentLogo]['w'],
        logoDimensions[currentLogo]['h']
    );

    // reset alpha channel so text is not translucent
    ctx.globalAlpha = "1";

    // draw the text
    ctx.textBaseline = 'bottom';
    ctx.textAlign = 'left';
    ctx.fillStyle = currentTextColor;
    ctx.font = 'normal 20pt "Gotham SSm"';

    if (currentTextColor === 'white') {
        ctx.shadowColor = 'rgba(0,0,0,0.7)';
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
        ctx.shadowBlur = 10;
    }

    if (currentCopyright) {
        credit = buildCreditString();
    }

    var creditWidth = ctx.measureText(credit);
    ctx.fillText(
        credit,
        canvas.width - (creditWidth.width + elementPadding),
        canvas.height - elementPadding
    );

    validateForm();
}

/*
* Build the proper format for the credit based on current copyright
*/
var buildCreditString = function() {
    var creditString;
    var val = $copyrightHolder.val();

    if (val === 'npr') {
        if ($photographer.val() === '') {
            creditString = 'NPR';
        } else {
            creditString = $photographer.val() + '/NPR';
        }
    } else if (val === 'freelance') {
        creditString = $photographer.val() + ' for NPR';
        if ($photographer.val() !== '') {
            $photographer.parents('.form-group').removeClass('has-warning');
        } else {
            $photographer.parents('.form-group').addClass('has-warning');
        }
    } else if (val === 'ap') {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/AP';
        } else {
            creditString = 'AP';
        }
    } else if (val === 'getty') {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/Getty';
        } else {
            creditString = 'Getty';
        }
    } else {
        if ($photographer.val() !== '') {
            creditString = $photographer.val() + '/' + $source.val();
        } else {
            creditString = $source.val();
        }

        if ($source.val() !== '') {
            $source.parents('.form-group').removeClass('has-warning');
        } else {
            $source.parents('.form-group').addClass('has-warning');
        }
    }

    return creditString;
}

/*
* Check to see if any required fields have not been
* filled out before enabling saving
*/
var validateForm = function() {
    if ($('.has-warning').length === 0 && currentCopyright) {
        $save.removeAttr('disabled');
        $("body").off("contextmenu", "canvas");
    } else {
        $save.attr('disabled', '');
        $("body").on("contextmenu", "canvas", function(e) {
            return false;
        });
    }
}

/*
* Handle dragging the image for crops when applicable
*/
var onDrag = function(e) {
    e.preventDefault();
    var originY = e.clientY;
    var startY = dy;

    if (currentCrop === 'original') {
        return;
    }

    function update(e) {
        if (Math.abs(e.clientY - originY) > 1) {
            dy = startY - (originY - e.clientY);

            // Prevent dragging image below upper bound
            if (dy > 0) {
                dy = 0;
                return;
            }

            // Prevent dragging image above lower bound
            if (dy < canvas.height - scaledImageHeight) {
                dy = canvas.height - scaledImageHeight;
                return;
            }

            renderCanvas();
        }
    }

    // Perform drag sequence:
    $(document).on('mousemove.drag', _.debounce(update, 5, true))
        .on('mouseup.drag', function(e) {
            $(document).off('mouseup.drag mousemove.drag');
            update(e);
        });
}

/*
* Take an image from file input and load it
*/
var handleImage = function(e) {
    var reader = new FileReader();
    reader.onload = function(e){
        image = e.target.result;
        imageFilename = $('.fileinput-filename').text().split('.')[0];
        img.src = image;
        $customFilename.text(imageFilename);
    }
    reader.readAsDataURL(e.target.files[0]);
}

/*
* Load the logo based on radio buttons
*/
var loadLogo = function() {
    logo.src = 'assets/logo-' + currentLogo + '-' + currentLogoColor + '.png';
}

/*
* Download the image on save click
*/
var onSaveClick = function(e) {
    e.preventDefault();

    /// create an "off-screen" anchor tag
    var link = document.createElement('a'),
        e;

    /// the key here is to set the download attribute of the a tag
    if ($customFilename.text()) {
        imageFilename = $customFilename.text();
    }
    link.download =  'twitterbug-' + imageFilename + '.png';

    /// convert canvas content to data-uri for link. When download
    /// attribute is set the content pointed to by link will be
    /// pushed as "download" in HTML5 capable browsers
    link.href = canvas.toDataURL();
    link.target = "_blank";

    /// create a "fake" click-event to trigger the download
    if (document.createEvent) {

        e = document.createEvent("MouseEvents");
        e.initMouseEvent("click", true, true, window,
                         0, 0, 0, 0, 0, false, false, false,
                         false, 0, null);

        link.dispatchEvent(e);

    } else if (link.fireEvent) {
        link.fireEvent("onclick");
    }
}

/*
* Handle logo radio button clicks
*/
var onLogoColorChange = function(e) {
    currentLogoColor = $(this).val();

    loadLogo();
    renderCanvas();
}

/*
* Handle text color radio button clicks
*/
var onTextColorChange = function(e) {
    currentTextColor = $(this).val();

    renderCanvas();
}

/*
* Handle logo radio button clicks
*/
var onLogoChange = function(e) {
    currentLogo = $(this).val();

    loadLogo();
    renderCanvas();
}

/*
* Handle crop radio button clicks
*/
var onCropChange = function() {
    currentCrop = $(this).val();

    if (currentCrop !== 'original') {
        $canvas.addClass('is-draggable');
        $dragHelp.show();
    } else {
        $canvas.removeClass('is-draggable');
        $dragHelp.hide();
    }

    renderCanvas();
}

/*
* Show the appropriate fields based on the chosen copyright
*/
var onCopyrightChange = function() {
    currentCopyright = $copyrightHolder.val();
    $photographer.parents('.form-group').removeClass('has-warning');
    $source.parents('.form-group').removeClass('has-warning');

    if (currentCopyright === 'npr') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group').slideUp();
    } else if (currentCopyright === 'freelance') {
        $photographer.parents('.form-group').slideDown();
        $source.parents('.form-group').slideUp();
        $photographer.parents('.form-group').addClass('has-warning required');
    } else if (currentCopyright === 'ap' || currentCopyright === 'getty') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group')
            .slideUp()
            .removeClass('has-warning required');

    } else if (currentCopyright === 'third-party') {
        $photographer.parents('.form-group').removeClass('required').slideDown();
        $source.parents('.form-group').slideDown();
        $source.parents('.form-group').addClass('has-warning required');
    } else {
        credit = '';
        $photographer.parents('.form-group').slideUp();
        $source.parents('.form-group')
            .slideUp()
            .parents('.form-group').removeClass('has-warning required');
    }

    renderCanvas();
}

$(onDocumentLoad);<|endoftext|>


<commit_before>/**
 * Tatoeba Project, free collaborative creation of multilingual corpuses project
 * Copyright (C) 2010 Allan SIMON <allan.simon@supinfo.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 *
 */
function get_language_interface_from_url() {
    pathArray = window.location.pathname.split('/');
    return pathArray[1];
}

function get_tatoeba_root_url() {
    var host = self.location.host;
    var interfaceLang = get_language_interface_from_url();

    return "//" + host + "/"+ interfaceLang;
}

function get_csrf_token() {
    // Adapted from https://stackoverflow.com/a/15724300
    // Licensed under CC BY-SA 4.0
    var value = "; " + document.cookie;
    var parts = value.split("; csrfToken=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}

/**
 * Returns string without the new lines and stuff.
 */
function normalized_sentence(sentenceText) {
    var reg = new RegExp("[\\t\\n\\r ]+", "gi");
    sentenceText = sentenceText.replace(reg, " ");
    return sentenceText;
}

/**
 * Change the language of the interface.
 */
function changeInterfaceLang(newLang) {
    // Saving the cookie
    var date = new Date();
    date.setMonth(date.getMonth()+1);
    document.cookie = 'CakeCookie[interfaceLanguage]=' + newLang
        + '; path=/'
        + '; expires=' + date.toGMTString();
    location.reload();
}

/**
 * Traverses through paginated pages on Ctrl + Left/Right arrow
 * Only activated when focus is not on a textbox.
 */

function key_navigation() {
    $(document).bind("keydown", function(event) {
        // handle right page turn. 39 = char code for right arrow.
        if(event.ctrlKey && event.which == 39 && document.activeElement.type != "text" && document.activeElement.type != "textarea") {
            $("div.paging span.current").next().children("a")[0].click();
        }
        //handle left page turn. 37 = left arrow
        if(event.ctrlKey && event.which == 37 && document.activeElement.type != "text" && document.activeElement.type != "textarea") {
            $("div.paging span.current").prev().children("a")[0].click();
        }
    });
}

$(document).ready(function() {
    //shortcuts only show up if pagination is present
    if ($("div.paging").length > 0) {
        key_navigation();
    }
});

$(document).ready(function() {
    $(document).watch("addrule", function() {
        $('.sentenceContent .text').each(function() {
            var sentence = $(this);
            if (sentence.data('text') === undefined) {
                sentence.data('text', sentence.text());
            }
        });
    });
});

/**
 * Polyfills
 */
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
      value: function(predicate) {
       // 1. Let O be ? ToObject(this value).
        if (this == null) {
          throw new TypeError('"this" is null or not defined');
        }

        var o = Object(this);

        // 2. Let len be ? ToLength(? Get(O, "length")).
        var len = o.length >>> 0;

        // 3. If IsCallable(predicate) is false, throw a TypeError exception.
        if (typeof predicate !== 'function') {
          throw new TypeError('predicate must be a function');
        }

        // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
        var thisArg = arguments[1];

        // 5. Let k be 0.
        var k = 0;

        // 6. Repeat, while k < len
        while (k < len) {
          // a. Let Pk be ! ToString(k).
          // b. Let kValue be ? Get(O, Pk).
          // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
          // d. If testResult is true, return kValue.
          var kValue = o[k];
          if (predicate.call(thisArg, kValue, k, o)) {
            return kValue;
          }
          // e. Increase k by 1.
          k++;
        }

        // 7. Return undefined.
        return undefined;
      },
      configurable: true,
      writable: true
    });
  }

// Fix for Chrome: prevent copy-pasting furigana when selecting a sentence
// https://stackoverflow.com/questions/13438391
$(document).on('copy', function (e) {
    // ClipboardJS uses a textarea to implement copying in Firefox.
    // We shouldn't mess with that event.
    if(e.target.tagName == 'TEXTAREA') return;

    var sel = window.getSelection();
    if (sel.rangeCount > 0) {
        e.preventDefault();
        $('rt').css('visibility', 'hidden');
        e.originalEvent.clipboardData.setData('text', sel.toString());
        $('rt').css('visibility', 'visible');
    }
});
<commit_msg>Don't try to change clipboard data in IE (fixes #2142)<commit_after>/**
 * Tatoeba Project, free collaborative creation of multilingual corpuses project
 * Copyright (C) 2010 Allan SIMON <allan.simon@supinfo.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 *
 */
function get_language_interface_from_url() {
    pathArray = window.location.pathname.split('/');
    return pathArray[1];
}

function get_tatoeba_root_url() {
    var host = self.location.host;
    var interfaceLang = get_language_interface_from_url();

    return "//" + host + "/"+ interfaceLang;
}

function get_csrf_token() {
    // Adapted from https://stackoverflow.com/a/15724300
    // Licensed under CC BY-SA 4.0
    var value = "; " + document.cookie;
    var parts = value.split("; csrfToken=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}

/**
 * Returns string without the new lines and stuff.
 */
function normalized_sentence(sentenceText) {
    var reg = new RegExp("[\\t\\n\\r ]+", "gi");
    sentenceText = sentenceText.replace(reg, " ");
    return sentenceText;
}

/**
 * Change the language of the interface.
 */
function changeInterfaceLang(newLang) {
    // Saving the cookie
    var date = new Date();
    date.setMonth(date.getMonth()+1);
    document.cookie = 'CakeCookie[interfaceLanguage]=' + newLang
        + '; path=/'
        + '; expires=' + date.toGMTString();
    location.reload();
}

/**
 * Traverses through paginated pages on Ctrl + Left/Right arrow
 * Only activated when focus is not on a textbox.
 */

function key_navigation() {
    $(document).bind("keydown", function(event) {
        // handle right page turn. 39 = char code for right arrow.
        if(event.ctrlKey && event.which == 39 && document.activeElement.type != "text" && document.activeElement.type != "textarea") {
            $("div.paging span.current").next().children("a")[0].click();
        }
        //handle left page turn. 37 = left arrow
        if(event.ctrlKey && event.which == 37 && document.activeElement.type != "text" && document.activeElement.type != "textarea") {
            $("div.paging span.current").prev().children("a")[0].click();
        }
    });
}

$(document).ready(function() {
    //shortcuts only show up if pagination is present
    if ($("div.paging").length > 0) {
        key_navigation();
    }
});

$(document).ready(function() {
    $(document).watch("addrule", function() {
        $('.sentenceContent .text').each(function() {
            var sentence = $(this);
            if (sentence.data('text') === undefined) {
                sentence.data('text', sentence.text());
            }
        });
    });
});

/**
 * Polyfills
 */
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
      value: function(predicate) {
       // 1. Let O be ? ToObject(this value).
        if (this == null) {
          throw new TypeError('"this" is null or not defined');
        }

        var o = Object(this);

        // 2. Let len be ? ToLength(? Get(O, "length")).
        var len = o.length >>> 0;

        // 3. If IsCallable(predicate) is false, throw a TypeError exception.
        if (typeof predicate !== 'function') {
          throw new TypeError('predicate must be a function');
        }

        // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
        var thisArg = arguments[1];

        // 5. Let k be 0.
        var k = 0;

        // 6. Repeat, while k < len
        while (k < len) {
          // a. Let Pk be ! ToString(k).
          // b. Let kValue be ? Get(O, Pk).
          // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
          // d. If testResult is true, return kValue.
          var kValue = o[k];
          if (predicate.call(thisArg, kValue, k, o)) {
            return kValue;
          }
          // e. Increase k by 1.
          k++;
        }

        // 7. Return undefined.
        return undefined;
      },
      configurable: true,
      writable: true
    });
  }

// Fix for Chrome: prevent copy-pasting furigana when selecting a sentence
// https://stackoverflow.com/questions/13438391
$(document).on('copy', function (e) {
    // ClipboardJS uses a textarea to implement copying in Firefox.
    // We shouldn't mess with that event.
    if(e.target.tagName == 'TEXTAREA') return;

    var sel = window.getSelection();
    if (sel.rangeCount > 0) {
        $('rt').css('visibility', 'hidden');
        var clipboardData = e.originalEvent.clipboardData;
        if (clipboardData) { // not available in IE
            e.preventDefault();
            clipboardData.setData('text', sel.toString());
        }
        $('rt').css('visibility', 'visible');
    }
});
<|endoftext|>


<commit_before>var util = require('util');
var moment = require('moment');
var MarkdownDocument = require('./markdown').MarkdownDocument;
var fmt = require('./formats');
var markzero = require('markzero');
var manual = markzero.manual;
var marked = markzero.marked;
var Parser = markzero.Parser;
var ManualRenderer = markzero.ManualRenderer;

/**
 *  A stream that buffers the contents of the document
 *  so that it may be converted from markdown to a man
 *  page once the document has been constructed.
 *
 *  Attempting to stream the conversion could result in
 *  some very strange behaviour.
 */
function ManDocumentStream() {
  this.content = '';
}

ManDocumentStream.prototype.write = function(data) {
  this.content += data;
}

/**
 *  Help document that outputs a man page.
 *
 *  Thie implementation extends the markdown format
 *  and used the markzero renderer to perform the conversion
 *  after buffering the markdown representation of the document.
 */
var ManDocument = function() {
  MarkdownDocument.apply(this, arguments);
  this.format = fmt.MAN_FORMAT;
}

util.inherits(ManDocument, MarkdownDocument);

/**
 *  Print the page header.
 *
 *  @param data The program data.
 *  @param stream The output stream.
 */
ManDocument.prototype.page = function(data, stream) {
  //var section = typeof(this.conf.section) === 'number'
    //? this.conf.section : 1;
  //section = Math.min(Math.max(section, 1), 9);
  this.header(manual.header(manual.DESCRIPTION), data, stream, true);
  this.print(stream);
}

/**
 *  @override
 */
ManDocument.prototype.titles = function() {
  MarkdownDocument.prototype.titles.apply(this, arguments);
  this.headers[MarkdownDocument.DESCRIPTION] = true;
}

/**
 *  @override
 */
ManDocument.prototype.write = function(data, stream) {
  stream = stream || process.stdout;
  var buffer = new ManDocumentStream;
  MarkdownDocument.prototype.write.call(this, data, buffer);
  var lexer = new marked.Lexer();
  var tokens = lexer.lex(buffer.content);
  var renderer = new ManualRenderer;
  var parser = new Parser({renderer: renderer, mangle: false});
  var dt = moment().format('MMMM YYYY');
  var page = manual.preamble({name: this.cli.name(), date: dt});
  page += parser.parse(tokens);
  stream.write(page);
}

module.exports = function(program) {
  return new ManDocument(program);
}

module.exports.ManDocument = ManDocument;
<commit_msg>Use full name in man page preamble.<commit_after>var util = require('util');
var moment = require('moment');
var MarkdownDocument = require('./markdown').MarkdownDocument;
var fmt = require('./formats');
var markzero = require('markzero');
var manual = markzero.manual;
var marked = markzero.marked;
var Parser = markzero.Parser;
var ManualRenderer = markzero.ManualRenderer;

/**
 *  A stream that buffers the contents of the document
 *  so that it may be converted from markdown to a man
 *  page once the document has been constructed.
 *
 *  Attempting to stream the conversion could result in
 *  some very strange behaviour.
 */
function ManDocumentStream() {
  this.content = '';
}

ManDocumentStream.prototype.write = function(data) {
  this.content += data;
}

/**
 *  Help document that outputs a man page.
 *
 *  Thie implementation extends the markdown format
 *  and used the markzero renderer to perform the conversion
 *  after buffering the markdown representation of the document.
 */
var ManDocument = function() {
  MarkdownDocument.apply(this, arguments);
  this.format = fmt.MAN_FORMAT;
}

util.inherits(ManDocument, MarkdownDocument);

/**
 *  Print the page header.
 *
 *  @param data The program data.
 *  @param stream The output stream.
 */
ManDocument.prototype.page = function(data, stream) {
  //var section = typeof(this.conf.section) === 'number'
    //? this.conf.section : 1;
  //section = Math.min(Math.max(section, 1), 9);
  this.header(manual.header(manual.DESCRIPTION), data, stream, true);
  this.print(stream);
}

/**
 *  @override
 */
ManDocument.prototype.titles = function() {
  MarkdownDocument.prototype.titles.apply(this, arguments);
  this.headers[MarkdownDocument.DESCRIPTION] = true;
}

/**
 *  @override
 */
ManDocument.prototype.write = function(data, stream) {
  stream = stream || process.stdout;
  var buffer = new ManDocumentStream;
  MarkdownDocument.prototype.write.call(this, data, buffer);
  var lexer = new marked.Lexer();
  var tokens = lexer.lex(buffer.content);
  var renderer = new ManualRenderer;
  var parser = new Parser({renderer: renderer, mangle: false});
  var dt = moment().format('MMMM YYYY');
  var page = manual.preamble({name: this.cmd.getFullName(), date: dt});
  page += parser.parse(tokens);
  stream.write(page);
}

module.exports = function(program) {
  return new ManDocument(program);
}

module.exports.ManDocument = ManDocument;
<|endoftext|>


<commit_before>function runTest()
{
    FBTest.sysout("issue5506.START");

    FBTest.openNewTab(basePath + "html/5506/issue5506.html", function(win)
    {
        FBTest.openFirebug();
        FBTest.selectPanel("html");

        FBTest.selectElementInHtmlPanel("test2", function(node)
        {
            // Start markup editing.
            FBTest.clickToolbarButton(null, "fbToggleHTMLEditing");

            var contentEl = win.document.getElementById("content");

            var panelStatus = FW.Firebug.chrome.window.document.getElementById("fbPanelStatus");
            var panelNode = FBTest.getPanel("html").panelNode;
            var textArea = panelNode.querySelector("textarea");

            FBTest.focus(textArea);

            function replaceWith(str)
            {
                FBTest.sendShortcut("a", {accelKey: true});
                if (str === "")
                    FBTest.synthesizeKey("VK_BACK_SPACE", null, win);
                else
                    FBTest.sendString(str, textArea);
                FW.Firebug.Editor.update(true);
            }

            function verifyPath(name)
            {
                var buttons = panelStatus.querySelectorAll("toolbarbutton");
                FBTest.compare(name, buttons[0].label, "First element in the path must be \"" + name + "\".");
                FBTest.compare("html", buttons[buttons.length-1].label, "Last element in the path must be \"html\".");
            }

            function clickPath(ind, callback)
            {
                var buttons = panelStatus.querySelectorAll("toolbarbutton");

                // For whatever reason, newly created elements can't receive clicks immediately.
                setTimeout(function()
                {
                    buttons[ind].click();
                    callback();
                }, 500);
            }

            function verifyHtml(html)
            {
                FBTest.compare(html, contentEl.innerHTML, "The page's HTML must match.");
            }

            var orig = '< >  ';
            FBTest.compare(orig, textArea.value, "The original value must be correct.");
            verifyPath("b#test2");

            var str = "";
            replaceWith(str);
            verifyPath("b");

            var strDiv = '
a2' + str + 'b2
';
            verifyHtml('a1' + strDiv + 'b1');

            clickPath(0, function()
            {
                verifyPath("b");
                FBTest.compare("", textArea.value, "Only the element should be shown after clicking it in the element path.");

                clickPath(1, function()
                {
                    verifyPath("b");
                    FBTest.compare(strDiv, textArea.value, "The value must change to the div's contents.");

                    replaceWith("<b invalid=\"");
                    verifyPath("section#content");
                    verifyHtml("a1b1");

                    FBTest.compare(2, contentEl.childNodes.length, "There must be two text nodes in #content.");
                    contentEl.removeChild(contentEl.firstChild);
                    contentEl.removeChild(contentEl.firstChild);

                    var newEl = win.document.createElement("span");
                    contentEl.appendChild(newEl);

                    strDiv = "hi";
                    replaceWith(strDiv);
                    verifyPath("strong");
                    verifyHtml(strDiv + "");

                    // Stop markup edit mode.
                    FBTest.clickToolbarButton(null, "fbToggleHTMLEditing");

                    // XXX waitForHtmlMutation doesn't seem to work here
                    var maxTime = Date.now() + 2000;
                    function testSel()
                    {
                        var nodeBox = panelNode.querySelector(".nodeBox.selected");
                        var tc = nodeBox.textContent, wanted = "";
                        if (tc !== wanted && Date.now() < maxTime)
                        {
                            setTimeout(testSel, 10);
                        }
                        else
                        {
                            FBTest.compare(wanted, tc, "The edited element must get visibly selected afterwards.");
                            FBTest.testDone("issue5506.DONE");
                        }
                    }
                    testSel();
                });
            });
        });
    });
}
<commit_msg>Issue 7027: FBTest html/5506/issue5506.js fails and hangs the browser<commit_after>function runTest()
{
    FBTest.sysout("issue5506.START");

    FBTest.openNewTab(basePath + "html/5506/issue5506.html", function(win)
    {
        FBTest.openFirebug();
        FBTest.selectPanel("html");

        FBTest.selectElementInHtmlPanel("test2", function(node)
        {
            // Start markup editing.
            FBTest.clickToolbarButton(null, "fbToggleHTMLEditing");

            var contentEl = win.document.getElementById("content");

            var panelStatus = FW.Firebug.chrome.window.document.getElementById("fbPanelStatus");
            var panel = FBTest.getPanel("html");
            var editor = panel.localEditors.html;
            var focusedElement = panel.panelNode.ownerDocument.activeElement;

            function replaceWith(str)
            {
                // CodeMirror updates asynchronously when typing, which makes testing harder. Let's
                // cheat and poke synchronous internals since that is already tested elsewhere.
                editor.setValue(str);
                FW.Firebug.Editor.update(true);
            }

            function verifyPath(name)
            {
                var buttons = panelStatus.querySelectorAll("toolbarbutton");
                FBTest.compare(name, buttons[0].label, "First element in the path must be \"" + name + "\".");
                FBTest.compare("html", buttons[buttons.length-1].label, "Last element in the path must be \"html\".");
            }

            function clickPath(ind, callback)
            {
                var buttons = panelStatus.querySelectorAll("toolbarbutton");

                // For whatever reason, newly created elements can't receive clicks immediately.
                setTimeout(function()
                {
                    buttons[ind].click();
                    callback();
                }, 500);
            }

            function verifyHtml(html)
            {
                FBTest.compare(html, contentEl.innerHTML, "The page's HTML must match.");
            }

            var orig = '< >  ';
            FBTest.compare(orig, editor.getValue(), "The original value must be correct.");
            verifyPath("b#test2");

            var str = "";
            replaceWith(str);
            verifyPath("b");

            var strDiv = '
a2' + str + 'b2
';
            verifyHtml('a1' + strDiv + 'b1');

            clickPath(0, function()
            {
                verifyPath("b");
                FBTest.compare("", editor.getValue(), "Only the element should be shown after clicking it in the element path.");

                clickPath(1, function()
                {
                    verifyPath("b");
                    FBTest.compare(strDiv, editor.getValue(), "The value must change to the div's contents.");

                    replaceWith("<b invalid=\"");
                    verifyPath("section#content");
                    verifyHtml("a1b1");

                    FBTest.compare(2, contentEl.childNodes.length, "There must be two text nodes in #content.");
                    contentEl.removeChild(contentEl.firstChild);
                    contentEl.removeChild(contentEl.firstChild);

                    var newEl = win.document.createElement("span");
                    contentEl.appendChild(newEl);

                    strDiv = "hi";
                    replaceWith(strDiv);
                    verifyPath("strong");
                    verifyHtml(strDiv + "");

                    // Stop markup edit mode.
                    FBTest.clickToolbarButton(null, "fbToggleHTMLEditing");

                    // XXX waitForHtmlMutation doesn't seem to work here
                    var maxTime = Date.now() + 2000;
                    function testSel()
                    {
                        var nodeBox = panel.panelNode.querySelector(".nodeBox.selected");
                        var tc = nodeBox.textContent, wanted = "";
                        if (tc !== wanted && Date.now() < maxTime)
                        {
                            setTimeout(testSel, 10);
                        }
                        else
                        {
                            FBTest.compare(wanted, tc, "The edited element must get visibly selected afterwards.");
                            FBTest.testDone("issue5506.DONE");
                        }
                    }
                    testSel();
                });
            });
        });
    });
}
<|endoftext|>


<commit_before>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`

 send('getTotals')}>
    Together we’ve made ${state.totalCalls.toLocaleString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} calls

  `;
}
<commit_msg>.toLocaleString() expects a Number, not a string.<commit_after>const html = require('choo/html');

module.exports = (state, prev, send) => {
  return html`

 send('getTotals')}>
    Together we’ve made ${Number(state.totalCalls).toLocaleString()} calls

  `;
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'
import { Table, Tooltip } from 'antd'
import { fetchInterfaceColList, fetchCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import { formatTime } from '../../../../common.js'

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol,
      currCaseList: state.interfaceCol.currCaseList
    }
  },
  {
    fetchInterfaceColList,
    fetchCaseList,
    setColData
  }
)
@withRouter
export default class InterfaceColContent extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currCaseList: PropTypes.array,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const result = await this.props.fetchInterfaceColList(this.props.match.params.id)
    let { currColId } = this.props;
    const params = this.props.match.params;
    const { actionId } = params;
    currColId = +actionId ||
                result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
                result.payload.data.data[0]._id;
    this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
    this.props.fetchCaseList(currColId)
    this.props.setColData({currColId: +currColId, isShowCol: true})
  }

  componentWillReceiveProps(nextProps) {
    const { interfaceColList } = nextProps;
    const { actionId: oldCaseId, id } = this.props.match.params
    let newCaseId = nextProps.match.params.actionId
    if (!interfaceColList.find(item => +item._id === +newCaseId)) {
      this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
    } else if (oldCaseId !== newCaseId) {
      this.props.fetchCaseList(newCaseId);
      this.props.setColData({currColId: +newCaseId, isShowCol: true})
    }
  }

  render() {

    const { currCaseList } = this.props;

    const columns = [{
      title: '用例名称',
      dataIndex: 'casename',
      key: 'casename'
    }, {
      title: '接口路径',
      dataIndex: 'path',
      key: 'path',
      render: (path, record) => {
        return (
          <Tooltip title="跳转到对应接口">
            <a href={`/project/2909/interface/api/${record.interface_id}`}>{path || 'fdsf'}
          </Tooltip>
        )
      }
    }, {
      title: '请求方式',
      dataIndex: 'method',
      key: 'method'
    }, {
      title: '更新时间',
      dataIndex: 'up_time',
      key: 'up_time',
      render: (item) => {
        return {formatTime(item)}
      }
    }];

    return (

测试集合

    )
  }
}
<commit_msg>fix: projectId<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux';
import PropTypes from 'prop-types'
import { withRouter } from 'react-router'
import { Table, Tooltip } from 'antd'
import { fetchInterfaceColList, fetchCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import { formatTime } from '../../../../common.js'

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol,
      currCaseList: state.interfaceCol.currCaseList
    }
  },
  {
    fetchInterfaceColList,
    fetchCaseList,
    setColData
  }
)
@withRouter
export default class InterfaceColContent extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currCaseList: PropTypes.array,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const result = await this.props.fetchInterfaceColList(this.props.match.params.id)
    let { currColId } = this.props;
    const params = this.props.match.params;
    const { actionId } = params;
    currColId = +actionId ||
                result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
                result.payload.data.data[0]._id;
    this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
    this.props.fetchCaseList(currColId)
    this.props.setColData({currColId: +currColId, isShowCol: true})
  }

  componentWillReceiveProps(nextProps) {
    const { interfaceColList } = nextProps;
    const { actionId: oldCaseId, id } = this.props.match.params
    let newCaseId = nextProps.match.params.actionId
    if (!interfaceColList.find(item => +item._id === +newCaseId)) {
      this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
    } else if (oldCaseId !== newCaseId) {
      this.props.fetchCaseList(newCaseId);
      this.props.setColData({currColId: +newCaseId, isShowCol: true})
    }
  }

  render() {

    const { currCaseList } = this.props;

    const columns = [{
      title: '用例名称',
      dataIndex: 'casename',
      key: 'casename'
    }, {
      title: '接口路径',
      dataIndex: 'path',
      key: 'path',
      render: (path, record) => {
        return (
          <Tooltip title="跳转到对应接口">
            <a href={`/project/${record.project_id}/interface/api/${record.interface_id}`}>{path || 'fdsf'}
          </Tooltip>
        )
      }
    }, {
      title: '请求方式',
      dataIndex: 'method',
      key: 'method'
    }, {
      title: '更新时间',
      dataIndex: 'up_time',
      key: 'up_time',
      render: (item) => {
        return {formatTime(item)}
      }
    }];

    return (

测试集合

    )
  }
}
<|endoftext|>


<commit_before>/**
 *
 *
 * @summary 产生号码投注模块
 * */
function numberFactory(options) {
    var self = this;
    if (options === undefined) {
        var message = 'profit constructor have 1 argument.';
        console.error(message);
        throw new SyntaxError(message);
    }
    //上期开奖号码
    this.lastPrizeNumberString = options.lastPrizeNumberString;
    //上期期号
    this.lastPeriodNumberString = options.lastPeriodNumberString;
}

/**
 *
 *
 * @summary 根据上期的中奖号码 产生下一期的投注号码
 * @return {String} 返回投注号码字符
 * */
numberFactory.prototype.getInvestNumberString = function () {
    var self = this;

    /**
     * 完整后二100注
     * */
    function getTotalNumberArray() {
        var a = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], b = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        var totalArray = [];
        for (var i = 0; i < a.length; i++) {
            for (var j = 0; j < b.length; j++) {
                totalArray.push(a[i] + '' + b[j]);
            }
        }
        return totalArray;
    }

    /**
     *
     * 获取号码的0,1,2路类型
     * */
    function getNumber012Type(num) {
        var restNumber = num % 3;
        return restNumber;
    }

    //开奖号码信息
    var prizeFirst = Number(self.lastPrizeNumberString.charAt(0));
    var prizeSecond = Number(self.lastPrizeNumberString.charAt(1));
    var prizeThird = Number(self.lastPrizeNumberString.charAt(2));
    var prizeForth = Number(self.lastPrizeNumberString.charAt(3));//5
    var prizeFifth = Number(self.lastPrizeNumberString.charAt(4));
    //开奖期号信息
    var periodFirst = Number(self.lastPeriodNumberString.charAt(0));
    var periodSecond = Number(self.lastPeriodNumberString.charAt(1));
    var periodThird = Number(self.lastPeriodNumberString.charAt(2));
    var totalNumberArray = getTotalNumberArray();
    //杀十位个位和
    var sum = prizeForth + prizeFifth;
    var firstFilterArray = [];
    for (var i = 0; i < totalNumberArray.length; i++) {
        var item = totalNumberArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if ((first + second) == sum) {
            continue;
        }
        firstFilterArray.push(item);
    }
    //杀跨度
    var secondFilterArray = [];
    var betweenValue = Math.abs(prizeForth - prizeFifth);
    for (var i = 0; i < firstFilterArray.length; i++) {
        var item = firstFilterArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if (betweenValue == Math.abs(first - second)) {
            continue;
        }
        secondFilterArray.push(item);
    }
    //十位杀千位 个位万位
    var thirdFilterArray = [];
    for (var i = 0; i < secondFilterArray.length; i++) {
        var item = secondFilterArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if (prizeSecond == first || prizeFirst == second) {
            continue;
        }
        thirdFilterArray.push(item);
    }
    //当前号码的012路
    var forthFilterArray = [];
    //上期开奖号码后二的012路 倒杀
    var shiWei012Type = getNumber012Type(prizeForth);//十位012路类型
    var geWei012Type = getNumber012Type(prizeFifth);//个位012路类型
    for (var i = 0; i < thirdFilterArray.length; i++) {
        var item = secondFilterArray[i];
        var first012Type = getNumber012Type(Number(item.charAt(0)));
        var second012Type = getNumber012Type(Number(item.charAt(1)));
        if (geWei012Type == first012Type && shiWei012Type == second012Type) {
            continue;
        }
        forthFilterArray.push(item);
    }

    return forthFilterArray.join(' ');

};

/**
 *
 * @summary 上期的开奖号码是否满足当前投注条件
 * @return {Boolean} true满足条件 false:不满足条件
 * */
numberFactory.prototype.isNeededPrizeNumber = function () {
    var self = this;
    var first = Number(self.lastPrizeNumberString.charAt(0));
    var second = Number(self.lastPrizeNumberString.charAt(1));
    var third = Number(self.lastPrizeNumberString.charAt(2));
    var forth = Number(self.lastPrizeNumberString.charAt(3));//5
    var fifth = Number(self.lastPrizeNumberString.charAt(4));
    return true;
};

//Testing requirement.
//if(module){
//    module.exports = numberFactory;
//}<commit_msg>added test code<commit_after>/**
 *
 *
 * @summary 产生号码投注模块
 * */
function numberFactory(options) {
    var self = this;
    if (options === undefined) {
        var message = 'profit constructor have 1 argument.';
        console.error(message);
        throw new SyntaxError(message);
    }
    //上期开奖号码
    this.lastPrizeNumberString = options.lastPrizeNumberString;
    //上期期号
    this.lastPeriodNumberString = options.lastPeriodNumberString;
}

/**
 *
 *
 * @summary 根据上期的中奖号码 产生下一期的投注号码
 * @return {String} 返回投注号码字符
 * */
numberFactory.prototype.getInvestNumberString = function () {
    var self = this;

    /**
     * 完整后二100注
     * */
    function getTotalNumberArray() {
        var a = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], b = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        var totalArray = [];
        for (var i = 0; i < a.length; i++) {
            for (var j = 0; j < b.length; j++) {
                totalArray.push(a[i] + '' + b[j]);
            }
        }
        return totalArray;
    }

    /**
     *
     * 获取号码的0,1,2路类型
     * */
    function getNumber012Type(num) {
        var restNumber = num % 3;
        return restNumber;
    }

    /**
     *
     * 获取号码的奇偶属性 0:偶数，1，基数
     * */
    function getJiEouType(num){
        var restNumber = num % 2;
        return restNumber;
    }

    //开奖号码信息
    var prizeFirst = Number(self.lastPrizeNumberString.charAt(0));
    var prizeSecond = Number(self.lastPrizeNumberString.charAt(1));
    var prizeThird = Number(self.lastPrizeNumberString.charAt(2));
    var prizeForth = Number(self.lastPrizeNumberString.charAt(3));//5
    var prizeFifth = Number(self.lastPrizeNumberString.charAt(4));
    //开奖期号信息
    var periodFirst = Number(self.lastPeriodNumberString.charAt(0));
    var periodSecond = Number(self.lastPeriodNumberString.charAt(1));
    var periodThird = Number(self.lastPeriodNumberString.charAt(2));
    var totalNumberArray = getTotalNumberArray();
    //杀十位个位和
    var sum = prizeForth + prizeFifth;
    var firstFilterArray = [];
    for (var i = 0; i < totalNumberArray.length; i++) {
        var item = totalNumberArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if ((first + second) == sum) {
            continue;
        }
        firstFilterArray.push(item);
    }
    //杀跨度
    var secondFilterArray = [];
    var betweenValue = Math.abs(prizeForth - prizeFifth);
    for (var i = 0; i < firstFilterArray.length; i++) {
        var item = firstFilterArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if (betweenValue == Math.abs(first - second)) {
            continue;
        }
        secondFilterArray.push(item);
    }
    //十位杀千位 个位万位
    var thirdFilterArray = [];
    for (var i = 0; i < secondFilterArray.length; i++) {
        var item = secondFilterArray[i];
        var first = Number(item.charAt(0));
        var second = Number(item.charAt(1));
        if (prizeSecond == first || prizeFirst == second) {
            continue;
        }
        thirdFilterArray.push(item);
    }
    //当前号码的奇偶属性
    var forthFilterArray = [];
    //上期开奖号码后二奇偶 倒杀
    var shiWeiJiOuType = getJiEouType(prizeForth);//十位奇偶类型
    var geWeiJiOuType = getJiEouType(prizeFifth);//个位奇偶类型
    for (var i = 0; i < thirdFilterArray.length; i++) {
        var item = secondFilterArray[i];
        var firstJiEouType = getJiEouType(Number(item.charAt(0)));
        var secondJiEouType = getJiEouType(Number(item.charAt(1)));
        if (geWeiJiOuType == firstJiEouType && shiWeiJiOuType == secondJiEouType) {
            continue;
        }
        forthFilterArray.push(item);
    }

    return forthFilterArray.join(' ');

};

/**
 *
 * @summary 上期的开奖号码是否满足当前投注条件
 * @return {Boolean} true满足条件 false:不满足条件
 * */
numberFactory.prototype.isNeededPrizeNumber = function () {
    var self = this;
    var first = Number(self.lastPrizeNumberString.charAt(0));
    var second = Number(self.lastPrizeNumberString.charAt(1));
    var third = Number(self.lastPrizeNumberString.charAt(2));
    var forth = Number(self.lastPrizeNumberString.charAt(3));//5
    var fifth = Number(self.lastPrizeNumberString.charAt(4));
    return true;
};

//Testing requirement.
//if(module){
//    module.exports = numberFactory;
//}<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  computed,
  get,
  set
} = Ember;

export default Ember.Controller.extend({
  actions: {
    handleTrixEvent(event) {
      get(this, 'model').pushObject(event);
      let document = event.target.editor.getDocument();
      let raw = Trix.serializeToContentType(document, 'text/html')
      set(this, 'raw', raw);
    },
  }
});
<commit_msg>demo preload from HTML<commit_after>import Ember from 'ember';

const {
  computed,
  get,
  set
} = Ember;

export default Ember.Controller.extend({
  actions: {
    handleTrixEvent(event) {
      get(this, 'model').pushObject(event);
      let document = event.target.editor.getDocument();
      let raw = Trix.serializeToContentType(document, 'text/html')
      set(this, 'raw', raw);
    },

    handleTrixInitializeEvent(event) {
      this.send('handleTrixEvent', event);
      const editor = event.target.editor;

      const html = '
Text preloaded from html string
';
      editor.loadHTML(html);
    },
  }
});
<|endoftext|>


<commit_before>function error(msg) {
    alert("Error: " + msg);
}

function create() {
    var name = $("#name").val();
    $.post("/wiki/" + name, function(data) {
        window.location.href = name + "/";
    });
}

function save(wiki, page) {
    var content = $("#content").val();
    $.post("/wiki/" + wiki + "/" + page, content, function(data) {
        location.reload()
    });
}

function edit(wiki, page) {
    $("#action").attr('onclick','').unbind('click');
    $("#action").html("save");
    $("#action").click(function () { save(wiki, page) });

    $("#main").html("<textarea name=\"content\" id=\"content\">");

    $.get("/wiki/" + wiki + "/" + page, function(data) {
        $("#content").val(data["content"]);
    }).fail(function() {
        error("cannot access API");
    });
}
<commit_msg>Catch errors in interface<commit_after>function error(msg) {
    alert("Error: " + msg);
}

function create() {
    var name = $("#name").val();
    $.post("/wiki/" + name, function(data) {
        window.location.href = name + "/";
    }).fail(function() {
        error("cannot create wiki (API not available, invalid wiki name, or wiki already exists)");
    });
}

function save(wiki, page) {
    var content = $("#content").val();
    $.post("/wiki/" + wiki + "/" + page, content, function(data) {
        location.reload()
    }).fail(function() {
        error("cannot save page (API not available or wiki is password-protected)");
    });
}

function edit(wiki, page) {
    $("#action").attr('onclick','').unbind('click');
    $("#action").html("save");
    $("#action").click(function () { save(wiki, page) });

    $("#main").html("<textarea name=\"content\" id=\"content\">");

    $.get("/wiki/" + wiki + "/" + page, function(data) {
        $("#content").val(data["content"]);
    }).fail(function() {
        error("cannot access API");
    });
}
<|endoftext|>