<commit_before>var LEFT_COL_SIZE = 180;
var HEIGHT = 60;
var videosSelector = '.swfObject';
var placeEl = document.querySelector('.clearfix.nubContainer.rNubContainer');

var destineEl = document.querySelector('#youtubeVideos');
if (!destineEl) {
    destineEl = document.createElement('div');
    destineEl.setAttribute('id', 'youtubeVideos');
    destineEl.style.position = 'fixed';
    destineEl.style.bottom = '28px';
    destineEl.style.left = '0px';
    placeEl.appendChild(destineEl);
}

function squizeVideo(element) {
    // Resize elements to the destineEl size

    if (!element.dataOriginalWidth) {
        element.dataOriginalWidth = element.style.width;
    }
    element.style.width = LEFT_COL_SIZE + 'px';

    if (!element.dataOriginalHeight) {
        element.dataOriginalHeight = element.style.height;
    }
    element.style.height = HEIGHT + 'px';

    var iframe = element.querySelector('iframe');

    if (!iframe.dataOriginalWidth) {
        iframe.dataOriginalWidth = iframe.getAttribute('width');
    }
    iframe.setAttribute('width', LEFT_COL_SIZE);

    if (!iframe.dataOriginalHeight) {
        iframe.dataOriginalHeight = iframe.getAttribute('height');
    }
    iframe.setAttribute('height', HEIGHT);
}

function squizeResetVideo(element) {
    element.style.width = element.dataOriginalWidth + 'px';
    element.style.height = element.dataOriginalHeight + 'px';
    iframe = element.querySelector('iframe');
    iframe.setAttribute('width', iframe.dataOriginalWidth);
    iframe.setAttribute('height', iframe.dataOriginalHeight);
}

function onMouseEnter(event) {
    // mouseEnter handler, will resize to original size, and change zIndex;

    squizeResetVideo(this);
    this.style.position = 'relative';
    this.style.bottom = (parseInt(this.dataOriginalHeight) - 60) + 'px';

}

function onMouseLeave(event) {
    // mouseEnter handler, will resize to original size, and change zIndex;
    squizeVideo(this);

    this.style.position = 'initial';
    this.style.bottom = '0px';
}

setInterval(function () {
    var videos = document.querySelectorAll(videosSelector);
    Array.prototype.forEach.call(videos, function(element) {
        if (element.parentElement !== destineEl) {
            destineEl.insertBefore(element);

            squizeVideo(element);
            element.addEventListener('mouseenter', onMouseEnter);
            element.addEventListener('mouseleave', onMouseLeave);
        }
    });
}, 1e3);
<commit_msg>Added support to viewport, so it only move the video if is out of viewport<commit_after>var LEFT_COL_SIZE = 180;
var HEIGHT = 60;
var videosSelector = '.swfObject';
var placeEl = document.querySelector('.clearfix.nubContainer.rNubContainer');

var destineEl = document.querySelector('#youtubeVideos');
if (!destineEl) {
    destineEl = document.createElement('div');
    destineEl.setAttribute('id', 'youtubeVideos');
    destineEl.style.position = 'fixed';
    destineEl.style.bottom = '28px';
    destineEl.style.left = '0px';
    placeEl.appendChild(destineEl);
}

function squizeVideo(element) {
    // Resize elements to the destineEl size

    if (!element.dataOriginalWidth) {
        element.dataOriginalWidth = element.style.width;
    }
    element.style.width = LEFT_COL_SIZE + 'px';

    if (!element.dataOriginalHeight) {
        element.dataOriginalHeight = element.style.height;
    }
    element.style.height = HEIGHT + 'px';

    if (!element.dataOriginalParent) {
        element.dataOriginalParent = element.parentElement;
    }

    var iframe = element.querySelector('iframe');

    if (!iframe.dataOriginalWidth) {
        iframe.dataOriginalWidth = iframe.getAttribute('width');
    }
    iframe.setAttribute('width', LEFT_COL_SIZE);

    if (!iframe.dataOriginalHeight) {
        iframe.dataOriginalHeight = iframe.getAttribute('height');
    }
    iframe.setAttribute('height', HEIGHT);
}

function squizeResetVideo(element) {
    element.style.width = element.dataOriginalWidth + 'px';
    element.style.height = element.dataOriginalHeight + 'px';
    iframe = element.querySelector('iframe');
    iframe.setAttribute('width', iframe.dataOriginalWidth);
    iframe.setAttribute('height', iframe.dataOriginalHeight);
}

function onMouseEnter(event) {
    // mouseEnter handler, will resize to original size, and change zIndex;

    squizeResetVideo(this);
    this.style.position = 'relative';
    this.style.bottom = (parseInt(this.dataOriginalHeight) - 60) + 'px';

}

function onMouseLeave(event) {
    // mouseEnter handler, will resize to original size, and change zIndex;
    squizeVideo(this);

    this.style.position = 'initial';
    this.style.bottom = '0px';
}

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

setInterval(function () {
    var videos = document.querySelectorAll(videosSelector);
    Array.prototype.forEach.call(videos, function(element) {
        if (!isElementInViewport(element) && element.parentElement !== destineEl) {
            destineEl.insertBefore(element);

            squizeVideo(element);
            element.addEventListener('mouseenter', onMouseEnter);
            element.addEventListener('mouseleave', onMouseLeave);
        }
    });
}, 1e3);
<|endoftext|>


<commit_before>//Config file for dev purpose
{
    parsers: [{
        name: "raw parser",
        worker: function() {
            return {
                run: function() {
                    return [{
                        name: "the file",
                        path: ".../.../",
                        data: ["111", "222", "333"]
                    }];
                }
            }
        }
    }],
    processors: [{
        name: "dumb processor",
        worker: function() {
            return {
                run: function(data) {
                    return data;
                }
            };
        }
    }],
    reporters: [{
        name: "console",
        worker: function() {
            return {
                run: function(data) {
                    console.log(data);
                }
            };
        }
    }]
}
<commit_msg>correct dev sample file<commit_after>//Config file for dev purpose
module.exports = {
    parsers: [{
        name: "raw parser",
        worker: function() {
            return {
                run: function() {
                    return [{
                        name: "the file",
                        path: ".../.../",
                        data: ["111", "222", "333"]
                    }];
                }
            }
        }
    }],
    processors: [{
        name: "dumb processor",
        worker: function() {
            return {
                run: function(data) {
                    return data;
                }
            };
        }
    }],
    reporters: [{
        name: "console",
        worker: function() {
            return {
                run: function(data) {
                    console.log(data);
                }
            };
        }
    }]
};<|endoftext|>


<commit_before>$.fn.zerobox = function(options) {
    options = $.extend({
        preLoad: true
    }, options);

    return this.each(function() {
        var $this = $(this);

        $this.click(function(e) {
            e.preventDefault();

            var $img = $this.find('img'),
                imgSrc = this.href,
                offset = $img.offset();

            var $zerobox = $('
')
                .css({
                    position: 'fixed',
                    top: offset.top - $(window).scrollTop(),
                    left: offset.left - $(window).scrollLeft(),
                    height: $img.height(),
                    width: $img.width(),
                    zIndex: 1099,
                    background: 'rgba(0, 0, 0, .75) url("' + imgSrc + '")',
                    backgroundRepeat: 'no-repeat',
                    backgroundPosition: 'center',
                    cursor: 'pointer',
                    textAlign: 'center'
                })
                .click(function() { $zerobox.remove(); })
                .appendTo(document.body).animate({
                    left: 0, top: 0, width: '100%', height: '100%'
                });

        });
        if (options.preLoad) {
            $this.one('mouseover', function() {
                // preload image
                var image = new Image();

                image.src = this.href;
            });
        }
    });
};
<commit_msg>added background animation<commit_after>$.fn.zerobox = function(options) {
    options = $.extend({
        preLoad: true
    }, options);

    return this.each(function() {
        var $this = $(this),
            preloadedImage;

        $this.click(function(e) {
            e.preventDefault();

            var $img = $this.find('img'),
                imgSrc = this.href,
                offset = $img.offset();

            var css = {
                position: 'fixed',
                top: offset.top - $(window).scrollTop(),
                left: offset.left - $(window).scrollLeft(),
                height: $img.height(),
                width: $img.width(),
                zIndex: 1099,
                background: 'rgba(0, 0, 0, .75) url("' + imgSrc + '")',
                backgroundRepeat: 'no-repeat',
                backgroundPosition: 'center',
                cursor: 'pointer',
                textAlign: 'center'
            };

            if (preloadedImage) {
                var thumbWidth = $img.width(),
                    thumbHeight = $img.height(),
                    fullWidth = preloadedImage.width,
                    fullHeight = preloadedImage.height;

                css['backgroundSize'] = thumbWidth + 'px ' + thumbHeight + 'px';
            }

            var $zerobox = $('
')
                .css(css)
                .click(function() { $zerobox.remove(); })
                .appendTo(document.body).animate({
                    left: 0, top: 0, width: '100%', height: '100%'
                });

            if (preloadedImage) {
                var $animatedDiv = $('
').css({width: thumbWidth, height: thumbHeight});

                $animatedDiv.animate({width : fullWidth, height: fullHeight }, {step: function() {
                    $zerobox.css('backgroundSize', $animatedDiv.width() + 'px ' + $animatedDiv.height() + 'px');
                }});
            }

        });
        if (options.preLoad) {
            $this.one('mouseover', function() {
                // preload image
                var image = new Image();

                image.onload = function() { preloadedImage = image; };
                image.src = this.href;
            });
        }
    });
};
<|endoftext|>


<commit_before>const expect = require('expect.js');
const Table = require('../');
const React = require('react');
const ReactDOM = require('react-dom');
const $ = require('jquery');
import './PagingColumns.spec';

describe('table', function() {
  const div = document.createElement('div');
  document.body.appendChild(div);

  const columns = [
    {title: '表头1', dataIndex: 'a', colSpan: 2, width: 100, render: function(o, row, index) {
      // 第一列中第一行合并两列
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.colSpan = 2;
      }
      return obj;
    }},
    {id: '123', title: '表头2', colSpan: 0, dataIndex: 'b', width: 100, render: function(o, row, index) {
      // 2列被合并掉了colSpan:0，第二列中第一行合并两行rowSpan:2
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.colSpan = 0;
      }
      return obj;
    }},
    {title: '表头3', dataIndex: 'c', width: 200, render: function(o, row, index) {
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.rowSpan = 2;
      }
      if (index === 1) {
        obj.props.rowSpan = 0;
      }
      return obj;
    }},
    {
      title: '操作', dataIndex: '', renderer: function() {
        return 操作;
      },
    },
  ];
  const data = [{a: '123'}, {a: 'cdd', b: 'edd'}, {a: '1333', c: 'eee', d: 2}];
  let node = $(div);

  beforeEach(function() {
    ReactDOM.render(

,
      div
    );
    node = $(div);
  });

  afterEach(function() {
    ReactDOM.unmountComponentAtNode(div);
  });

  it('simple works', function() {
    expect(node.find('table').length).to.be(1);
    expect(node.find('tbody tr').length).to.be(data.length);
  });

  it('th colSpan works', function() {
    expect(node.find('table').length).to.be(1);
    let colspanNum = 0;
    const cLen = columns.length;
    for (let i = 0; i < cLen; i++) {
      const headerColSpan = columns[i].colSpan;
      if (headerColSpan) {
        colspanNum += headerColSpan;
      }
    }
    expect(node.find('thead th').length).to.be(columns.length - (colspanNum - 1));
  });

  it('td colSpan works', function() {
    expect(node.find('table').length).to.be(1);

    // 第一行第一列合并了
    const trFstTds = node.find('tbody tr:first td').length;
    // 最后一行是没有合并的
    const trLstTds = node.find('tbody tr:last td').length;
    // 检查未合并列是否受到影响
    expect(trLstTds).to.be(columns.length);
    // 合并和未合并行中列数是否相等
    expect(trFstTds).to.not.be(trLstTds);
    // 合并的列数是否正确
    expect(trFstTds).to.be(columns.length - ( 2 - 1));
  });

  it('td rowSpan works', function() {
    expect(node.find('table').length).to.be(1);
    const rowspanNum = 2;
    expect(node.find('tbody tr').eq(1).find('td').length).to.be(columns.length - (rowspanNum - 1));
  });

  it('should has pager for columns', function() {

  });
});
<commit_msg>Add test cases for column render<commit_after>const expect = require('expect.js');
const Table = require('../');
const React = require('react');
const ReactDOM = require('react-dom');
const $ = require('jquery');
import './PagingColumns.spec';

describe('table', function() {
  const div = document.createElement('div');
  document.body.appendChild(div);

  const columns = [
    {title: '表头1', dataIndex: 'a', key: 'a', colSpan: 2, width: 100, render: function(o, row, index) {
      // 第一列中第一行合并两列
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.colSpan = 2;
      }
      return obj;
    }},
    {id: '123', title: '表头2', colSpan: 0, dataIndex: 'b', key: 'b', width: 100, render: function(o, row, index) {
      // 2列被合并掉了colSpan:0，第二列中第一行合并两行rowSpan:2
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.colSpan = 0;
      }
      return obj;
    }},
    {title: '表头3', dataIndex: 'c', key: 'c', width: 200, render: function(o, row, index) {
      const obj = {
        children: o,
        props: {},
      };
      if (index === 0) {
        obj.props.rowSpan = 2;
      }
      if (index === 1) {
        obj.props.rowSpan = 0;
      }
      return obj;
    }},
    {
      title: 'operation', dataIndex: '', key: 'operation', render: function() {
        return 操作;
      },
    },
    {
      title: 'number', dataIndex: '', key: 'number', render: function() {
        return 123;
      },
    },
    {
      title: 'zero', dataIndex: '', key: 'zero', render: function() {
        return 0;
      },
    },
    {
      title: 'empty string', dataIndex: '', key: 'empty-string', render: function() {
        return '';
      },
    },
    {
      title: 'string', dataIndex: '', key: 'string', render: function() {
        return 'text';
      },
    },
    {
      title: 'false', dataIndex: '', key: 'false', render: function() {
        return false;
      },
    },
    {
      title: 'Array', dataIndex: '', key: 'array', render: function() {
        return [操作1, 操作2];
      },
    },
  ];
  const data = [{a: '123', key: '1'}, {a: 'cdd', b: 'edd', key: '2'}, {a: '1333', c: 'eee', d: 2, key: '3'}];
  let node = $(div);

  beforeEach(function() {
    ReactDOM.render(

,
      div
    );
    node = $(div);
  });

  afterEach(function() {
    ReactDOM.unmountComponentAtNode(div);
  });

  it('simple works', function() {
    expect(node.find('table').length).to.be(1);
    expect(node.find('tbody tr').length).to.be(data.length);
  });

  it('th colSpan works', function() {
    expect(node.find('table').length).to.be(1);
    let colspanNum = 0;
    const cLen = columns.length;
    for (let i = 0; i < cLen; i++) {
      const headerColSpan = columns[i].colSpan;
      if (headerColSpan) {
        colspanNum += headerColSpan;
      }
    }
    expect(node.find('thead th').length).to.be(columns.length - (colspanNum - 1));
  });

  it('td colSpan works', function() {
    expect(node.find('table').length).to.be(1);

    // 第一行第一列合并了
    const trFstTds = node.find('tbody tr:first td').length;
    // 最后一行是没有合并的
    const trLstTds = node.find('tbody tr:last td').length;
    // 检查未合并列是否受到影响
    expect(trLstTds).to.be(columns.length);
    // 合并和未合并行中列数是否相等
    expect(trFstTds).to.not.be(trLstTds);
    // 合并的列数是否正确
    expect(trFstTds).to.be(columns.length - ( 2 - 1));
  });

  it('td rowSpan works', function() {
    expect(node.find('table').length).to.be(1);
    const rowspanNum = 2;
    expect(node.find('tbody tr').eq(1).find('td').length).to.be(columns.length - (rowspanNum - 1));
  });

  it('should render columns', function() {
    const trLstTds = node.find('tbody tr:last td');
    expect(trLstTds.eq(3).find('a').length).to.be(1); // jsx
    expect(trLstTds.eq(4).text()).to.be('123'); // number
    expect(trLstTds.eq(5).text()).to.be('0'); // 0
    expect(trLstTds.eq(6).text()).to.be(''); // empty string
    expect(trLstTds.eq(7).text()).to.be('text'); // string
    expect(trLstTds.eq(8).text()).to.be(''); // false
    expect(trLstTds.eq(9).find('a').length).to.be(2); // array
  });
});
<|endoftext|>


<commit_before>import Taro, { Component } from '@tarojs/taro'
import { View, Image, Text } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import { AtAvatar, AtIcon } from 'taro-ui'
import { NAVIGATE_TYPE } from '../../constants/navigateType'
import { GLOBAL_CONFIG } from '../../constants/globalConfig'
import { baseUrl } from '../../service/config'
import userAction from '../../actions/user'
import { hasLogin } from '../../utils/common'

import './index.less'
import api from "../../service/api";

class Index extends Component {

  config = {
    navigationBarTitleText: '',
    navigationBarBackgroundColor: '#2d8cf0',
    navigationBarTextStyle: 'white',
    enablePullDownRefresh: true
  }

  constructor(props) {
    super(props)
    this.state = {
      isLogin: false
    }
  }

  componentWillReceiveProps(nextProps) {
    console.log(this.props, nextProps)
  }

  componentDidMount() {
    Taro.showLoading({title: GLOBAL_CONFIG.LOADING_TEXT})
    this.getUserInfo()
  }

  componentWillUnmount() {
  }

  componentDidShow() {
    this.setState({
      isLogin: hasLogin()
    })
  }

  componentDidHide() {
  }

  onPullDownRefresh() {
    this.getUserInfo()
  }

  getUserInfo() {
    if (hasLogin()) {
      userAction.getUserInfo().then(()=>{
        Taro.hideLoading()
        Taro.stopPullDownRefresh()
      })
    } else {
      Taro.hideLoading()
      Taro.stopPullDownRefresh()
    }
  }

  handleNavigate(type) {
    switch (type) {
      case NAVIGATE_TYPE.REPOS: {
        let url = encodeURI(baseUrl + '/user/repos')
        Taro.navigateTo({
          url: '/pages/repo/repoList?url=' + url
        })
      }
      break
      case NAVIGATE_TYPE.FOLLOWERS: {
        Taro.navigateTo({
          url: '/pages/account/follow?type=followers'
        })
      }
      break
      case NAVIGATE_TYPE.FOLLOWING: {
        Taro.navigateTo({
          url: '/pages/account/follow?type=following'
        })
      }
      break
      case NAVIGATE_TYPE.STARRED_REPOS: {
        Taro.navigateTo({
          url: '/pages/repo/starredRepo'
        })
      }
      break
      case NAVIGATE_TYPE.ISSUES: {
        Taro.navigateTo({
          url: '/pages/repo/issues?url=/user/issues'
        })
      }
        break
      case NAVIGATE_TYPE.ABOUT: {
        Taro.navigateTo({
          url: '/pages/account/about'
        })
      }
        break
      case NAVIGATE_TYPE.STAR: {
        this.handleStar()
      }
        break
      default: {
      }
    }
  }

  login() {
    Taro.navigateTo({
      url: '/pages/login/login'
    })
  }

  handleStar() {
    Taro.showLoading({title: GLOBAL_CONFIG.LOADING_TEXT})
    let url = '/user/starred/huangjianke/Gitter'
    api.put(url).then((res)=>{
      Taro.hideLoading()
      if (res.statusCode === 204) {
        Taro.showToast({
          title: 'Thank you!',
          icon: 'success'
        })
      }
    })
  }

  render() {
    const { isLogin } = this.state
    const { userInfo } = this.props
    return (
      <View>
        {
          isLogin ? (
            <View className='content'>
              <Image className='account_bg' src={require('../../assets/images/account_bg.png')}/>
              <View className='user_info'>
                <AtAvatar className='avatar' circle image={userInfo.avatar_url}/>
                <Text className='username'>{userInfo.name}</Text>
                <View className='login_name'>@{userInfo.login}</View>
              </View>
              <View className='info_view'>
                {userInfo.bio.length > 0 && <View className='bio'>{userInfo.bio}</View>}
                <View className='item_view'>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.REPOS)}>
                    <View className='title'>{userInfo.public_repos}+{userInfo.owned_private_repos}</View>
                    <View className='desc'>Repos</View>
                  </View>
                  <View className='line'/>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.FOLLOWERS)}>
                    <View className='title'>{userInfo.followers}</View>
                    <View className='desc'>Followers</View>
                  </View>
                  <View className='line'/>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.FOLLOWING)}>
                    <View className='title'>{userInfo.following}</View>
                    <View className='desc'>Following</View>
                  </View>
                </View>
              </View>
              <View className='list_view'>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.STARRED_REPOS)}>
                  <View className='list_title'>Starred Repos</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.ISSUES)}>
                  <View className='list_title'>Issues</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
              </View>
              <View className='list_view'>
                <View className='list'>
                  <View className='list_title'>Email</View>
                  <View className='list_content'>{userInfo.email.length > 0 ? userInfo.email : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Blog</View>
                  <View className='list_content'>{userInfo.blog.length > 0 ? userInfo.blog : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Company</View>
                  <View className='list_content'>{userInfo.company.length > 0 ? userInfo.company : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Location</View>
                  <View className='list_content'>{userInfo.location.length > 0 ? userInfo.location : '--'}</View>
                </View>
              </View>
              <View className='list_view'>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.STAR)}>
                  <View className='list_title'>Star Gitter</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.ABOUT)}>
                  <View className='list_title'>About</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
              </View>
              <View className='bottom_view' />
            </View>
          ) : (
            <View className='content'>
              <Image mode='aspectFit'
                     className='logo'
                     src={require('../../assets/images/logo.png')} />
              <View className='login_button'
                    onClick={this.login.bind(this)}>
                Login
              </View>
            </View>
          )
        }
      </View>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    userInfo: state.user.userInfo
  }
}
export default connect(mapStateToProps)(Index)
<commit_msg>昵称为空时不展示<commit_after>import Taro, { Component } from '@tarojs/taro'
import { View, Image, Text } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import { AtAvatar, AtIcon } from 'taro-ui'
import { NAVIGATE_TYPE } from '../../constants/navigateType'
import { GLOBAL_CONFIG } from '../../constants/globalConfig'
import { baseUrl } from '../../service/config'
import userAction from '../../actions/user'
import { hasLogin } from '../../utils/common'

import './index.less'
import api from "../../service/api";

class Index extends Component {

  config = {
    navigationBarTitleText: '',
    navigationBarBackgroundColor: '#2d8cf0',
    navigationBarTextStyle: 'white',
    enablePullDownRefresh: true
  }

  constructor(props) {
    super(props)
    this.state = {
      isLogin: false
    }
  }

  componentWillReceiveProps(nextProps) {
    console.log(this.props, nextProps)
  }

  componentDidMount() {
    Taro.showLoading({title: GLOBAL_CONFIG.LOADING_TEXT})
    this.getUserInfo()
  }

  componentWillUnmount() {
  }

  componentDidShow() {
    this.setState({
      isLogin: hasLogin()
    })
  }

  componentDidHide() {
  }

  onPullDownRefresh() {
    this.getUserInfo()
  }

  getUserInfo() {
    if (hasLogin()) {
      userAction.getUserInfo().then(()=>{
        Taro.hideLoading()
        Taro.stopPullDownRefresh()
      })
    } else {
      Taro.hideLoading()
      Taro.stopPullDownRefresh()
    }
  }

  handleNavigate(type) {
    switch (type) {
      case NAVIGATE_TYPE.REPOS: {
        let url = encodeURI(baseUrl + '/user/repos')
        Taro.navigateTo({
          url: '/pages/repo/repoList?url=' + url
        })
      }
      break
      case NAVIGATE_TYPE.FOLLOWERS: {
        Taro.navigateTo({
          url: '/pages/account/follow?type=followers'
        })
      }
      break
      case NAVIGATE_TYPE.FOLLOWING: {
        Taro.navigateTo({
          url: '/pages/account/follow?type=following'
        })
      }
      break
      case NAVIGATE_TYPE.STARRED_REPOS: {
        Taro.navigateTo({
          url: '/pages/repo/starredRepo'
        })
      }
      break
      case NAVIGATE_TYPE.ISSUES: {
        Taro.navigateTo({
          url: '/pages/repo/issues?url=/user/issues'
        })
      }
        break
      case NAVIGATE_TYPE.ABOUT: {
        Taro.navigateTo({
          url: '/pages/account/about'
        })
      }
        break
      case NAVIGATE_TYPE.STAR: {
        this.handleStar()
      }
        break
      default: {
      }
    }
  }

  login() {
    Taro.navigateTo({
      url: '/pages/login/login'
    })
  }

  handleStar() {
    Taro.showLoading({title: GLOBAL_CONFIG.LOADING_TEXT})
    let url = '/user/starred/huangjianke/Gitter'
    api.put(url).then((res)=>{
      Taro.hideLoading()
      if (res.statusCode === 204) {
        Taro.showToast({
          title: 'Thank you!',
          icon: 'success'
        })
      }
    })
  }

  render() {
    const { isLogin } = this.state
    const { userInfo } = this.props
    return (
      <View>
        {
          isLogin ? (
            <View className='content'>
              <Image className='account_bg' src={require('../../assets/images/account_bg.png')}/>
              <View className='user_info'>
                <AtAvatar className='avatar' circle image={userInfo.avatar_url}/>
                {
                  userInfo.name.length > 0 &&
                  <Text className='username'>{userInfo.name}</Text>
                }
                <View className='login_name'>@{userInfo.login}</View>
              </View>
              <View className='info_view'>
                {userInfo.bio.length > 0 && <View className='bio'>{userInfo.bio}</View>}
                <View className='item_view'>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.REPOS)}>
                    <View className='title'>{userInfo.public_repos}+{userInfo.owned_private_repos}</View>
                    <View className='desc'>Repos</View>
                  </View>
                  <View className='line'/>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.FOLLOWERS)}>
                    <View className='title'>{userInfo.followers}</View>
                    <View className='desc'>Followers</View>
                  </View>
                  <View className='line'/>
                  <View className='item' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.FOLLOWING)}>
                    <View className='title'>{userInfo.following}</View>
                    <View className='desc'>Following</View>
                  </View>
                </View>
              </View>
              <View className='list_view'>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.STARRED_REPOS)}>
                  <View className='list_title'>Starred Repos</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.ISSUES)}>
                  <View className='list_title'>Issues</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
              </View>
              <View className='list_view'>
                <View className='list'>
                  <View className='list_title'>Email</View>
                  <View className='list_content'>{userInfo.email.length > 0 ? userInfo.email : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Blog</View>
                  <View className='list_content'>{userInfo.blog.length > 0 ? userInfo.blog : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Company</View>
                  <View className='list_content'>{userInfo.company.length > 0 ? userInfo.company : '--'}</View>
                </View>
                <View className='list'>
                  <View className='list_title'>Location</View>
                  <View className='list_content'>{userInfo.location.length > 0 ? userInfo.location : '--'}</View>
                </View>
              </View>
              <View className='list_view'>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.STAR)}>
                  <View className='list_title'>Star Gitter</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
                <View className='list' onClick={this.handleNavigate.bind(this, NAVIGATE_TYPE.ABOUT)}>
                  <View className='list_title'>About</View>
                  <AtIcon prefixClass='ion' value='ios-arrow-forward' size='18' color='#7f7f7f' />
                </View>
              </View>
              <View className='bottom_view' />
            </View>
          ) : (
            <View className='content'>
              <Image mode='aspectFit'
                     className='logo'
                     src={require('../../assets/images/logo.png')} />
              <View className='login_button'
                    onClick={this.login.bind(this)}>
                Login
              </View>
            </View>
          )
        }
      </View>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    userInfo: state.user.userInfo
  }
}
export default connect(mapStateToProps)(Index)
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas 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, version 3 of the License.
 *
 * Canvas 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/>.
 */

import {AlertManagerContext} from '@canvas/alerts/react/AlertManager'
import {CREATE_DISCUSSION_ENTRY} from '../graphql/Mutations'
import {DISCUSSION_QUERY} from '../graphql/Queries'
import {DiscussionTopicToolbarContainer} from './containers/DiscussionTopicToolbarContainer/DiscussionTopicToolbarContainer'
import {DiscussionTopicRepliesContainer} from './containers/DiscussionTopicRepliesContainer/DiscussionTopicRepliesContainer'
import {DiscussionTopicContainer} from './containers/DiscussionTopicContainer/DiscussionTopicContainer'
import errorShipUrl from '@canvas/images/ErrorShip.svg'
import GenericErrorPage from '@canvas/generic-error-page'
import {getOptimisticResponse} from './utils'
import {HIGHLIGHT_TIMEOUT, PER_PAGE, SearchContext} from './utils/constants'
import I18n from 'i18n!discussion_topics_post'
import {IsolatedViewContainer} from './containers/IsolatedViewContainer/IsolatedViewContainer'
import LoadingIndicator from '@canvas/loading-indicator'
import {NoResultsFound} from './components/NoResultsFound/NoResultsFound'
import PropTypes from 'prop-types'
import React, {useContext, useEffect, useState} from 'react'
import {useMutation, useQuery} from 'react-apollo'

const DiscussionTopicManager = props => {
  const [searchTerm, setSearchTerm] = useState('')
  const [filter, setFilter] = useState('all')
  const [sort, setSort] = useState('desc')
  const [pageNumber, setPageNumber] = useState(0)
  const searchContext = {
    searchTerm,
    setSearchTerm,
    filter,
    setFilter,
    sort,
    setSort,
    pageNumber,
    setPageNumber
  }

  const goToTopic = () => {
    setSearchTerm('')
    closeIsolatedView()
    setIsTopicHighlighted(true)
  }

  // Isolated View State
  const [isolatedEntryId, setIsolatedEntryId] = useState(null)
  const [replyFromId, setReplyFromId] = useState(null)
  const [isolatedViewOpen, setIsolatedViewOpen] = useState(false)
  const [editorExpanded, setEditorExpanded] = useState(false)

  // Highlight State
  const [isTopicHighlighted, setIsTopicHighlighted] = useState(false)
  const [highlightEntryId, setHighlightEntryId] = useState(null)
  const [relativeEntryId, setRelativeEntryId] = useState(null)

  useEffect(() => {
    if (isTopicHighlighted) {
      setTimeout(() => {
        setIsTopicHighlighted(false)
      }, HIGHLIGHT_TIMEOUT)
    }
  }, [isTopicHighlighted])

  useEffect(() => {
    if (highlightEntryId) {
      setTimeout(() => {
        setHighlightEntryId(null)
      }, HIGHLIGHT_TIMEOUT)
    }
  }, [highlightEntryId])

  const openIsolatedView = (discussionEntryId, isolatedEntryId, withRCE, relativeId = null) => {
    setReplyFromId(discussionEntryId)
    setIsolatedEntryId(isolatedEntryId || discussionEntryId)
    setIsolatedViewOpen(true)
    setEditorExpanded(withRCE)
    setRelativeEntryId(relativeId)
  }

  const closeIsolatedView = () => {
    setIsolatedViewOpen(false)
  }

  const {setOnFailure, setOnSuccess} = useContext(AlertManagerContext)
  const variables = {
    discussionID: props.discussionTopicId,
    perPage: PER_PAGE,
    page: btoa(pageNumber * PER_PAGE),
    searchTerm,
    rootEntries: !searchTerm && filter === 'all',
    filter,
    sort,
    courseID: window.ENV?.course_id
  }

  const discussionTopicQuery = useQuery(DISCUSSION_QUERY, {
    variables,
    fetchPolicy: searchTerm ? 'no-cache' : 'cache-and-network'
  })

  const updateDraftCache = (cache, result) => {
    try {
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables}
      }
      const newDiscussionEntryDraft = result.data.createDiscussionEntryDraft.discussionEntryDraft
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      if (currentDiscussion && newDiscussionEntryDraft) {
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes =
          currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.filter(
            draft => draft.id != newDiscussionEntryDraft.id
          )
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.push(
          newDiscussionEntryDraft
        )

        cache.writeQuery({...options, data: currentDiscussion})
      }
    } catch (e) {
      // do nothing for errors updating the cache on a draft
    }
  }

  const removeDraftFromDiscussionCache = (cache, result) => {
    try {
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables}
      }
      const newDiscussionEntry = result.data.createDiscussionEntry.discussionEntry
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes =
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.filter(
          draft =>
            draft.rootEntryId != newDiscussionEntry.rootEntryId &&
            draft.discussionTopicID != newDiscussionEntry.discussionTopicID
        )
      cache.writeQuery({...options, data: currentDiscussion})
    } catch (e) {
      // do nothing for errors updating the cache on a draft
    }
  }

  const updateCache = (cache, result) => {
    try {
      const lastPage = discussionTopicQuery.data.legacyNode.entriesTotalPages - 1
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables, page: btoa(lastPage * PER_PAGE)}
      }
      const newDiscussionEntry = result.data.createDiscussionEntry.discussionEntry
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      if (currentDiscussion && newDiscussionEntry) {
        currentDiscussion.legacyNode.entryCounts.repliesCount += 1
        removeDraftFromDiscussionCache(cache, result)
        if (variables.sort === 'desc') {
          currentDiscussion.legacyNode.discussionEntriesConnection.nodes.unshift(newDiscussionEntry)
        } else {
          currentDiscussion.legacyNode.discussionEntriesConnection.nodes.push(newDiscussionEntry)
        }

        cache.writeQuery({...options, data: currentDiscussion})
      }
    } catch (e) {
      discussionTopicQuery.refetch(variables)
    }
  }

  const [createDiscussionEntry] = useMutation(CREATE_DISCUSSION_ENTRY, {
    update: updateCache,
    onCompleted: data => {
      setOnSuccess(I18n.t('The discussion entry was successfully created.'))
      setHighlightEntryId(data.createDiscussionEntry.discussionEntry._id)
      if (sort === 'asc') {
        setPageNumber(discussionTopicQuery.data.legacyNode.entriesTotalPages - 1)
      }
    },
    onError: () => {
      setOnFailure(I18n.t('There was an unexpected error creating the discussion entry.'))
    }
  })

  if (discussionTopicQuery.loading && !searchTerm && filter === 'all') {
    return <LoadingIndicator />
  }

  if (discussionTopicQuery.error || !discussionTopicQuery?.data?.legacyNode) {
    return (
      <GenericErrorPage
        imageUrl={errorShipUrl}
        errorSubject={I18n.t('Discussion Topic initial query error')}
        errorCategory={I18n.t('Discussion Topic Post Error Page')}
      />
    )
  }

  return (
    <SearchContext.Provider value={searchContext}>
      <DiscussionTopicToolbarContainer discussionTopic={discussionTopicQuery.data.legacyNode} />
      <DiscussionTopicContainer
        updateDraftCache={updateDraftCache}
        discussionTopic={discussionTopicQuery.data.legacyNode}
        createDiscussionEntry={text => {
          createDiscussionEntry({
            variables: {
              discussionTopicId: ENV.discussion_topic_id,
              message: text
            },
            optimisticResponse: getOptimisticResponse(text)
          })
        }}
        isHighlighted={isTopicHighlighted}
      />
      {discussionTopicQuery.data.legacyNode.discussionEntriesConnection.nodes.length === 0 &&
      (searchTerm || filter === 'unread') ? (
        <NoResultsFound />
      ) : (
        <DiscussionTopicRepliesContainer
          discussionTopic={discussionTopicQuery.data.legacyNode}
          updateDraftCache={updateDraftCache}
          removeDraftFromDiscussionCache={removeDraftFromDiscussionCache}
          onOpenIsolatedView={(
            discussionEntryId,
            isolatedEntryId,
            withRCE,
            relativeId,
            highlightId
          ) => {
            setHighlightEntryId(highlightId)
            openIsolatedView(discussionEntryId, isolatedEntryId, withRCE, relativeId)
          }}
          goToTopic={goToTopic}
          highlightEntryId={highlightEntryId}
        />
      )}
      {ENV.isolated_view && isolatedEntryId && (
        <IsolatedViewContainer
          relativeEntryId={relativeEntryId}
          removeDraftFromDiscussionCache={removeDraftFromDiscussionCache}
          updateDraftCache={updateDraftCache}
          discussionTopic={discussionTopicQuery.data.legacyNode}
          discussionEntryId={isolatedEntryId}
          replyFromId={replyFromId}
          open={isolatedViewOpen}
          RCEOpen={editorExpanded}
          setRCEOpen={setEditorExpanded}
          onClose={closeIsolatedView}
          onOpenIsolatedView={openIsolatedView}
          goToTopic={goToTopic}
          highlightEntryId={highlightEntryId}
          setHighlightEntryId={setHighlightEntryId}
        />
      )}
    </SearchContext.Provider>
  )
}

DiscussionTopicManager.propTypes = {
  discussionTopicId: PropTypes.string.isRequired
}

export default DiscussionTopicManager
<commit_msg>Clean up DiscussionTopicManager<commit_after>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas 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, version 3 of the License.
 *
 * Canvas 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/>.
 */

import {AlertManagerContext} from '@canvas/alerts/react/AlertManager'
import {CREATE_DISCUSSION_ENTRY} from '../graphql/Mutations'
import {DISCUSSION_QUERY} from '../graphql/Queries'
import {DiscussionTopicToolbarContainer} from './containers/DiscussionTopicToolbarContainer/DiscussionTopicToolbarContainer'
import {DiscussionTopicRepliesContainer} from './containers/DiscussionTopicRepliesContainer/DiscussionTopicRepliesContainer'
import {DiscussionTopicContainer} from './containers/DiscussionTopicContainer/DiscussionTopicContainer'
import errorShipUrl from '@canvas/images/ErrorShip.svg'
import GenericErrorPage from '@canvas/generic-error-page'
import {getOptimisticResponse} from './utils'
import {HIGHLIGHT_TIMEOUT, PER_PAGE, SearchContext} from './utils/constants'
import I18n from 'i18n!discussion_topics_post'
import {IsolatedViewContainer} from './containers/IsolatedViewContainer/IsolatedViewContainer'
import LoadingIndicator from '@canvas/loading-indicator'
import {NoResultsFound} from './components/NoResultsFound/NoResultsFound'
import PropTypes from 'prop-types'
import React, {useContext, useEffect, useState} from 'react'
import {useMutation, useQuery} from 'react-apollo'

const DiscussionTopicManager = props => {
  const [searchTerm, setSearchTerm] = useState('')
  const [filter, setFilter] = useState('all')
  const [sort, setSort] = useState('desc')
  const [pageNumber, setPageNumber] = useState(0)
  const searchContext = {
    searchTerm,
    setSearchTerm,
    filter,
    setFilter,
    sort,
    setSort,
    pageNumber,
    setPageNumber
  }

  const goToTopic = () => {
    setSearchTerm('')
    closeIsolatedView()
    setIsTopicHighlighted(true)
  }

  // Isolated View State
  const [isolatedEntryId, setIsolatedEntryId] = useState(null)
  const [replyFromId, setReplyFromId] = useState(null)
  const [isolatedViewOpen, setIsolatedViewOpen] = useState(false)
  const [editorExpanded, setEditorExpanded] = useState(false)

  // Highlight State
  const [isTopicHighlighted, setIsTopicHighlighted] = useState(false)
  const [highlightEntryId, setHighlightEntryId] = useState(null)
  const [relativeEntryId, setRelativeEntryId] = useState(null)

  useEffect(() => {
    if (isTopicHighlighted) {
      setTimeout(() => {
        setIsTopicHighlighted(false)
      }, HIGHLIGHT_TIMEOUT)
    }
  }, [isTopicHighlighted])

  useEffect(() => {
    if (highlightEntryId) {
      setTimeout(() => {
        setHighlightEntryId(null)
      }, HIGHLIGHT_TIMEOUT)
    }
  }, [highlightEntryId])

  const openIsolatedView = (discussionEntryId, isolatedId, withRCE, relativeId = null) => {
    setReplyFromId(discussionEntryId)
    setIsolatedEntryId(isolatedId || discussionEntryId)
    setIsolatedViewOpen(true)
    setEditorExpanded(withRCE)
    setRelativeEntryId(relativeId)
  }

  const closeIsolatedView = () => {
    setIsolatedViewOpen(false)
  }

  const {setOnFailure, setOnSuccess} = useContext(AlertManagerContext)
  const variables = {
    discussionID: props.discussionTopicId,
    perPage: PER_PAGE,
    page: btoa(pageNumber * PER_PAGE),
    searchTerm,
    rootEntries: !searchTerm && filter === 'all',
    filter,
    sort,
    courseID: window.ENV?.course_id
  }

  const discussionTopicQuery = useQuery(DISCUSSION_QUERY, {
    variables,
    fetchPolicy: searchTerm ? 'no-cache' : 'cache-and-network'
  })

  const updateDraftCache = (cache, result) => {
    try {
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables}
      }
      const newDiscussionEntryDraft = result.data.createDiscussionEntryDraft.discussionEntryDraft
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      if (currentDiscussion && newDiscussionEntryDraft) {
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes =
          currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.filter(
            draft => draft.id !== newDiscussionEntryDraft.id
          )
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.push(
          newDiscussionEntryDraft
        )

        cache.writeQuery({...options, data: currentDiscussion})
      }
    } catch (e) {
      // do nothing for errors updating the cache on a draft
    }
  }

  const removeDraftFromDiscussionCache = (cache, result) => {
    try {
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables}
      }
      const newDiscussionEntry = result.data.createDiscussionEntry.discussionEntry
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes =
        currentDiscussion.legacyNode.discussionEntryDraftsConnection.nodes.filter(
          draft =>
            draft.rootEntryId !== newDiscussionEntry.rootEntryId &&
            draft.discussionTopicID !== newDiscussionEntry.discussionTopicID
        )
      cache.writeQuery({...options, data: currentDiscussion})
    } catch (e) {
      // do nothing for errors updating the cache on a draft
    }
  }

  const updateCache = (cache, result) => {
    try {
      const lastPage = discussionTopicQuery.data.legacyNode.entriesTotalPages - 1
      const options = {
        query: DISCUSSION_QUERY,
        variables: {...variables, page: btoa(lastPage * PER_PAGE)}
      }
      const newDiscussionEntry = result.data.createDiscussionEntry.discussionEntry
      const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

      if (currentDiscussion && newDiscussionEntry) {
        currentDiscussion.legacyNode.entryCounts.repliesCount += 1
        removeDraftFromDiscussionCache(cache, result)
        if (variables.sort === 'desc') {
          currentDiscussion.legacyNode.discussionEntriesConnection.nodes.unshift(newDiscussionEntry)
        } else {
          currentDiscussion.legacyNode.discussionEntriesConnection.nodes.push(newDiscussionEntry)
        }

        cache.writeQuery({...options, data: currentDiscussion})
      }
    } catch (e) {
      discussionTopicQuery.refetch(variables)
    }
  }

  const [createDiscussionEntry] = useMutation(CREATE_DISCUSSION_ENTRY, {
    update: updateCache,
    onCompleted: data => {
      setOnSuccess(I18n.t('The discussion entry was successfully created.'))
      setHighlightEntryId(data.createDiscussionEntry.discussionEntry._id)
      if (sort === 'asc') {
        setPageNumber(discussionTopicQuery.data.legacyNode.entriesTotalPages - 1)
      }
    },
    onError: () => {
      setOnFailure(I18n.t('There was an unexpected error creating the discussion entry.'))
    }
  })

  if (discussionTopicQuery.loading && !searchTerm && filter === 'all') {
    return <LoadingIndicator />
  }

  if (discussionTopicQuery.error || !discussionTopicQuery?.data?.legacyNode) {
    return (
      <GenericErrorPage
        imageUrl={errorShipUrl}
        errorSubject={I18n.t('Discussion Topic initial query error')}
        errorCategory={I18n.t('Discussion Topic Post Error Page')}
      />
    )
  }

  return (
    <SearchContext.Provider value={searchContext}>
      <DiscussionTopicToolbarContainer discussionTopic={discussionTopicQuery.data.legacyNode} />
      <DiscussionTopicContainer
        updateDraftCache={updateDraftCache}
        discussionTopic={discussionTopicQuery.data.legacyNode}
        createDiscussionEntry={text => {
          createDiscussionEntry({
            variables: {
              discussionTopicId: ENV.discussion_topic_id,
              message: text
            },
            optimisticResponse: getOptimisticResponse(text)
          })
        }}
        isHighlighted={isTopicHighlighted}
      />
      {discussionTopicQuery.data.legacyNode.discussionEntriesConnection.nodes.length === 0 &&
      (searchTerm || filter === 'unread') ? (
        <NoResultsFound />
      ) : (
        <DiscussionTopicRepliesContainer
          discussionTopic={discussionTopicQuery.data.legacyNode}
          updateDraftCache={updateDraftCache}
          removeDraftFromDiscussionCache={removeDraftFromDiscussionCache}
          onOpenIsolatedView={(discussionEntryId, isolatedId, withRCE, relativeId, highlightId) => {
            setHighlightEntryId(highlightId)
            openIsolatedView(discussionEntryId, isolatedId, withRCE, relativeId)
          }}
          goToTopic={goToTopic}
          highlightEntryId={highlightEntryId}
        />
      )}
      {ENV.isolated_view && isolatedEntryId && (
        <IsolatedViewContainer
          relativeEntryId={relativeEntryId}
          removeDraftFromDiscussionCache={removeDraftFromDiscussionCache}
          updateDraftCache={updateDraftCache}
          discussionTopic={discussionTopicQuery.data.legacyNode}
          discussionEntryId={isolatedEntryId}
          replyFromId={replyFromId}
          open={isolatedViewOpen}
          RCEOpen={editorExpanded}
          setRCEOpen={setEditorExpanded}
          onClose={closeIsolatedView}
          onOpenIsolatedView={openIsolatedView}
          goToTopic={goToTopic}
          highlightEntryId={highlightEntryId}
          setHighlightEntryId={setHighlightEntryId}
        />
      )}
    </SearchContext.Provider>
  )
}

DiscussionTopicManager.propTypes = {
  discussionTopicId: PropTypes.string.isRequired
}

export default DiscussionTopicManager
<|endoftext|>


<commit_before>define([
    "jquery",
    "../core/logging",
    "../registry",
    '../utils'
], function($, logging, registry, utils) {
    var log = logging.getLogger('form-state');

    var _ = {
        name: "form-state",
        trigger: "form.pat-form-state",
        init: function($form) {
            if ($form.length > 1)
                return $form.each(function() { _.init($(this)); });

            _.setReset.call($form);

            return $form;
        },
        setModified: function() {
            var $form = $(this);

            $form.find('[type=reset]').prop('disabled', false);
            $form.find('[type=submit]').prop('disabled', false);

            $form.removeClass("saved").addClass("modified")
                .off('.pat-form-state')
                .one('reset.pat-form-state', _.setReset)
                .one('pat-ajax-success.pat-form-state', _.setSaved);
            log.debug('modified');
        },
        setReset: function() {
            var $form = $(this);

            $form.find('[type=reset]').prop('disabled', true);
            $form.find('[type=submit]').prop('disabled', true);

            $form
                .removeClass("modified")
                .off('.pat-form-state')
                .one('change.pat-form-state textchange.pat-form-state',
                     _.setModified);
            log.debug('reset');
        },
        setSaved: function() {
            var $form = $(this);
            _.setReset.call($form);

            var time = new Date(),
                timestr = time.getHours() + ':'
                    + time.getMinutes() + ':'
                    + time.getSeconds();
            $form.find('time.last-saved').remove();
            $form.prepend(
                '<time class="last-saved" datetime="' + timestr + '">' +
                    timestr + '</time>'
            );

            $form.addClass("saved");
            log.debug('saved');
        }
    };
    registry.register(_);
});
<commit_msg>modified does not remove saved<commit_after>define([
    "jquery",
    "../core/logging",
    "../registry",
    '../utils'
], function($, logging, registry, utils) {
    var log = logging.getLogger('form-state');

    var _ = {
        name: "form-state",
        trigger: "form.pat-form-state",
        init: function($form) {
            if ($form.length > 1)
                return $form.each(function() { _.init($(this)); });

            _.setReset.call($form);

            return $form;
        },
        setModified: function() {
            var $form = $(this);

            $form.find('[type=reset]').prop('disabled', false);
            $form.find('[type=submit]').prop('disabled', false);

            $form.addClass("modified")
                .off('.pat-form-state')
                .one('reset.pat-form-state', _.setReset)
                .one('pat-ajax-success.pat-form-state', _.setSaved);
            log.debug('modified');
        },
        setReset: function() {
            var $form = $(this);

            $form.find('[type=reset]').prop('disabled', true);
            $form.find('[type=submit]').prop('disabled', true);

            $form
                .removeClass("modified")
                .off('.pat-form-state')
                .one('change.pat-form-state textchange.pat-form-state',
                     _.setModified);
            log.debug('reset');
        },
        setSaved: function() {
            var $form = $(this);
            _.setReset.call($form);

            var time = new Date(),
                timestr = time.getHours() + ':'
                    + time.getMinutes() + ':'
                    + time.getSeconds();
            $form.find('time.last-saved').remove();
            $form.prepend(
                '<time class="last-saved" datetime="' + timestr + '">' +
                    timestr + '</time>'
            );

            $form.addClass("saved");
            log.debug('saved');
        }
    };
    registry.register(_);
});
<|endoftext|>


<commit_before>define(["pex/core/Vec2", "pex/core/Context", "pex/core/Program", "pex/core/Vbo"], function(Vec2, Context, Program, Vbo) {
  var vert = ""
    + "attribute vec2 position;"
    + "attribute vec2 texCoord;"
    + "uniform vec2 windowSize;"
    + "uniform vec2 pixelPosition;"
    + "uniform vec2 pixelSize;"
    + "varying vec2 vTexCoord;"
    + "void main() {"
    +  "float x = -1.0 + 2.0 * (pixelPosition.x + pixelSize.x * position.x)/windowSize.x;"
    +  "float y = -1.0 + 2.0 * (pixelPosition.y + pixelSize.y * position.y)/windowSize.y;"
    +  "gl_Position = vec4(x, y, 0.0, 1.0);"
    +  "vTexCoord = texCoord;"
    + "}";

  var frag = ""
    + "varying vec2 vTexCoord;"
    + "uniform sampler2D tex0;"
    + "void main() {"
    +   "gl_FragColor = texture2D(tex0, vTexCoord);"
    + "}";

  function ScreenImage(screenWidth, screenHeight, x, y, w, h, tex) {
    var gl = Context.currentContext.gl;
    this.program = new Program(vert, frag);
    this.vbo = new Vbo(gl.TRIANGLES);
    this.tex = tex;
    this.windowSize = new Vec2(screenWidth, screenHeight);
    this.position = new Vec2(x, y);
    this.size = new Vec2(w, h);

    var vertices = [
       0, 0,
       0, 1,
       1, 1,
       1, 0
    ];

    var texCoords = [
       0,  0,
       0,  1,
       1,  1,
       1,  0
    ];

    var indices = [
      0, 2, 1,
      0, 3, 2
    ];

    this.vbo.addAttrib("position", vertices, 2);
    this.vbo.addAttrib("texCoord", texCoords, 2);
    this.vbo.setIndices(indices);
  }

  ScreenImage.prototype.setPosition = function(position) {
    this.position.x = position.x;
    this.position.y = position.y;
  }

  ScreenImage.prototype.setBounds = function(bounds) {
    this.position.x = bounds.x;
    this.position.y = bounds.y;
    this.size.x = bounds.width;
    this.size.y = bounds.height;
  }

  ScreenImage.prototype.setTexture = function(texture) {
    this.tex = texture;
  }

  ScreenImage.prototype.draw = function(program) {
    var gl = Context.currentContext.gl;
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    if (this.tex) this.tex.bind();
    program = program ? program : this.program;
    program.use();
    program.uniforms.windowSize(this.windowSize);
    program.uniforms.pixelPosition(this.position);
    program.uniforms.pixelSize(this.size);
    gl.disable(gl.DEPTH_TEST);
    this.vbo.draw(program);
    gl.disable(gl.BLEND);
    gl.enable(gl.DEPTH_TEST);
  }

  return ScreenImage;
});<commit_msg>Option to disable blending in ScreenImage<commit_after>define(["pex/core/Vec2", "pex/core/Context", "pex/core/Program", "pex/core/Vbo"], function(Vec2, Context, Program, Vbo) {
  var vert = ""
    + "attribute vec2 position;"
    + "attribute vec2 texCoord;"
    + "uniform vec2 windowSize;"
    + "uniform vec2 pixelPosition;"
    + "uniform vec2 pixelSize;"
    + "varying vec2 vTexCoord;"
    + "void main() {"
    +  "float x = -1.0 + 2.0 * (pixelPosition.x + pixelSize.x * position.x)/windowSize.x;"
    +  "float y = -1.0 + 2.0 * (pixelPosition.y + pixelSize.y * position.y)/windowSize.y;"
    +  "gl_Position = vec4(x, y, 0.0, 1.0);"
    +  "vTexCoord = texCoord;"
    + "}";

  var frag = ""
    + "varying vec2 vTexCoord;"
    + "uniform sampler2D tex0;"
    + "void main() {"
    +   "gl_FragColor = texture2D(tex0, vTexCoord);"
    + "}";

  function ScreenImage(screenWidth, screenHeight, x, y, w, h, tex, blending) {
    var gl = Context.currentContext.gl;
    this.program = new Program(vert, frag);
    this.vbo = new Vbo(gl.TRIANGLES);
    this.tex = tex;
    this.windowSize = new Vec2(screenWidth, screenHeight);
    this.position = new Vec2(x, y);
    this.size = new Vec2(w, h);
    this.blending = (blending !== undefined) ? blending : true;

    var vertices = [
       0, 0,
       0, 1,
       1, 1,
       1, 0
    ];

    var texCoords = [
       0,  0,
       0,  1,
       1,  1,
       1,  0
    ];

    var indices = [
      0, 2, 1,
      0, 3, 2
    ];

    this.vbo.addAttrib("position", vertices, 2);
    this.vbo.addAttrib("texCoord", texCoords, 2);
    this.vbo.setIndices(indices);
  }

  ScreenImage.prototype.setPosition = function(position) {
    this.position.x = position.x;
    this.position.y = position.y;
  }

  ScreenImage.prototype.setBounds = function(bounds) {
    this.position.x = bounds.x;
    this.position.y = bounds.y;
    this.size.x = bounds.width;
    this.size.y = bounds.height;
  }

  ScreenImage.prototype.setTexture = function(texture) {
    this.tex = texture;
  }

  ScreenImage.prototype.draw = function(program, dontBlend) {
    var gl = Context.currentContext.gl;
    if (this.blending) {
      gl.enable(gl.BLEND);
      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    }
    if (this.tex) this.tex.bind();
    program = program ? program : this.program;
    program.use();
    program.uniforms.windowSize(this.windowSize);
    program.uniforms.pixelPosition(this.position);
    program.uniforms.pixelSize(this.size);
    gl.disable(gl.DEPTH_TEST);
    this.vbo.draw(program);
    if (this.blending) {
      gl.disable(gl.BLEND);
    }
    gl.enable(gl.DEPTH_TEST);
  }

  return ScreenImage;
});<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path')
var assert = require('assert');
var Lawncipher = require('../');
var Long = require('long');
var faker = require('faker');

var rmdir = require('rmdir');

var sodium = require('libsodium-wrappers');
var to_hex = sodium.to_hex, from_hex = sodium.from_hex;

var deepObjectEquality = Lawncipher.deepObjectEquality;
var randomBuffer = Lawncipher.randomBuffer;

var PearsonBPlusTree = Lawncipher.PearsonBPlusTree;
var PearsonSeedGenerator = Lawncipher.PearsonSeedGenerator;
var PearsonHasher = Lawncipher.PearsonHasher;
var Index = Lawncipher.Index;

var testIndexPath = path.join(__dirname, 'test_index');

var testSeed = [
	98,  6, 85, 150, 36, 23, 112, 164, 135, 207, 169,  5, 26, 64, 165, 219,
	61, 20, 68, 89, 130, 63, 52, 102, 24, 229, 132, 245, 80, 216, 195, 115,
	90, 168, 156, 203, 177, 120,  2, 190, 188,  7, 100, 185, 174, 243, 162, 10,
	237, 18, 253, 225, 8, 208, 172, 244, 255, 126, 101, 79, 145, 235, 228, 121,
	123, 251, 67, 250, 161, 0, 107, 97, 241, 111, 181, 82, 249, 33, 69, 55,
	59,153, 29, 9, 213, 167, 84, 93, 30, 46, 94, 75, 151, 114, 73, 222,
	197, 96, 210, 45, 16, 227, 248, 202, 51, 152, 252, 125, 81, 206, 215, 186,
	39, 158, 178, 187, 131, 136,  1, 49, 50, 17, 141, 91, 47, 129, 60, 99,
	154, 35, 86, 171, 105, 34, 38, 200, 147, 58, 77, 118, 173, 246, 76, 254,
	133, 232, 196, 144, 198, 124, 53, 4, 108, 74, 223, 234, 134, 230, 157, 139,
	189, 205, 199, 128, 176, 19, 211, 236, 127, 192, 231, 70, 233, 88, 146, 44,
	183, 201, 22, 83, 13, 214, 116, 109, 159, 32, 95, 226, 140, 220, 57, 12,
	221, 31, 209, 182, 143, 92, 149, 184, 148, 62, 113, 65, 37, 27, 106, 166,
	3, 14, 204, 72, 21, 41, 56, 66, 28, 193, 40, 217, 25, 54,179,117,
	238, 87, 240, 155, 180, 170, 242, 212, 191, 163, 78, 218, 137, 194, 175, 110,
	43, 119, 224, 71, 122, 142, 42, 160, 104, 48, 247, 103, 15, 11, 138, 239
];

function arrayEquality(a1, a2){
	if (!(Array.isArray(a1) && Array.isArray(a2))) return false;
	if (a1.length != a2.length) return false;

	for (var i = 0; i < a1.length; i++){
		if (a1[i] != a2[i]) return false;
	}
	return true;
}

function newRandomUserDoc(){
	return {
		name: faker.name.firstName() + ' ' + faker.name.lastName(),
		email: faker.internet.email(),
		address: faker.address.streetName() + '\n' + faker.address.city() + '\n' + faker.address.country()
	};
}

function clock(s){
	if (!s) return process.hrtime();
	var d = process.hrtime(s);
	return Math.round(d[0] * 1000 + d[1] / 1000000);
}

function basicTests(next){
	var indexKey = randomBuffer(32);

	function saveIndex(_next){
		rmdir(testIndexPath, function(err){
			if (err) throw err;

			var testIndex = new Index(__dirname, 'test_index', 'index', indexKey, testSeed, function(loadErr){
				if (loadErr) throw loadErr;

				testIndex.add('test', 1, function(e){
					if (e) throw e;

					testIndex.add('hello', 2, function(e){
						if (e) throw e;

						_next();
					});
				});
			});
		});
	}

	function loadIndex(_next){
		var testIndex = new Index(__dirname, 'test_index', 'index', indexKey, testSeed, function(loadErr){
			if (loadErr) throw loadErr;

			testIndex.lookup('test', function(err, value){
				if (err) throw err;

				assert(value == 1);

				testIndex.lookup('hello', function(err, value){
					if (err) throw err;

					assert(value == 2);

					_next();
				});
			});
		});
	}

	saveIndex(function(){
		loadIndex(next);
	});
}

//A testing method, designed to test save, loading, lookup and removal of a non-negligeable data set
function loadTests(docCount, cb, usingNoTrigger){
	docCount = docCount || 1000;
	if (typeof cb != 'function') throw new TypeError('cb must be a function');

	var testIndex;
	var indexKey = randomBuffer(32);
	var indexSeed = PearsonSeedGenerator();

	var dataSet = new Array(docCount);

	console.log('Generating ' + docCount + ' documents');

	for (var i = 0; i < docCount; i++){
		dataSet[i] = {k: faker.random.uuid(), v: newRandomUserDoc()};
	}

	function saveIndex(_next){
		rmdir(testIndexPath, function(err){
			if (err) throw err;

			console.log('Starting index writes');
			var saveStart = clock();
			testIndex = new Index(__dirname, 'test_index', 'index', indexKey, indexSeed, function(loadErr){
				if (loadErr) throw loadErr;

				console.log('Saving ' + docCount + ' documents' + (usingNoTrigger ? ' (while using noTrigger = true)' : ''));

				if (usingNoTrigger){
					for (var i = 0; i < docCount - 1; i++){
						testIndex.add(dataSet[i].k, dataSet[i].v, undefined, true);
					}

					testIndex.add(dataSet[dataSet.length - 1].k, dataSet[dataSet.length - 1].v, function(err){
						if (err) throw err;

						var saveDuration = clock(saveStart);
						console.log('Saving ' + docCount + ' documents took ' + saveDuration + 'ms (noTrigger = true)');
						_next();
					});
				} else {
					//usingNoTrigger == false -> async everything

					var addIndex = 0;

					function addOne(){
						var currentTuple = dataSet[addIndex];
						testIndex.add(currentTuple.k, currentTuple.v, function(err){
							if (err) throw err;

							nextAdd();
						});
					}

					function nextAdd(){
						addIndex++;
						if (addIndex == docCount){
							var saveDuration = clock(saveStart);
							console.log('Saving ' + docCount + ' documents took ' + saveDuration + 'ms (noTrigger = false)')
							_next();
						} else {
							if (addIndex % 100 == 0) setTimeout(addOne, 0);
							else addOne();
						}
					}

					addOne();
				}
			});
		});
	}

	function loadIndex(_next){
		var loadStart = clock();
		testIndex = new Index(__dirname, 'test_index', 'index', indexKey, indexSeed, function(loadErr){
			if (loadErr) throw loadErr;

			console.log('Loading and looking up ' + docCount + ' documents');

			var lookupIndex = 0;

			function lookupOne(){
				var currentTuple = dataSet[lookupIndex];
				testIndex.lookup(currentTuple.k, function(err, value){
					if (err) throw err;

					//console.log('lookupIndex: ' + lookupIndex);
					//console.log('Found value: ' + JSON.stringify(value));
					//console.log('Expected value: ' + JSON.stringify(currentTuple.v));
					assert(deepObjectEquality(currentTuple.v, value));

					nextLookup();
				});
			}

			function nextLookup(){
				lookupIndex++;
				if (lookupIndex == docCount){
					var loadDuration = clock(loadStart);
					console.log('Loading and looking up ' + docCount + ' documents took ' + loadDuration + 'ms');
					_next();
				} else {
					if (lookupIndex % 100 == 0) setTimeout(lookupOne, 0);
					else lookupOne();
				}
			}

			lookupOne();
		});
	}

	function destroyIndex(_next){
		if (!testIndex) throw new Error('testIndex must be defined');

		console.log('Removing ' + docCount + ' documents');

		var destroyStart = clock();

		if (usingNoTrigger){
			for (var i = 0; i < docCount - 1; i++){
				testIndex.remove(dataSet[i].k, undefined, undefined, true);
			}

			testIndex.remove(dataSet[docCount - 1].k, undefined, function(err){
				if (err) throw err;

				var destroyDuration = clock(destroyStart);
				console.log('Removing ' + docCount + ' documents took ' + destroyDuration + 'ms (noTrigger = true)');
				_next();
			});

		} else {
			var removeIndex = 0;

			function removeOne(){
				var currentTuple = dataSet[removeIndex];
				testIndex.remove(currentTuple.k, undefined, function(err){
					if (err) throw err;

					nextRemoval();
				}, usingNoTrigger && removeIndex < docCount - 1);
			}

			function nextRemoval(){
				removeIndex++;
				if (removeIndex == docCount){
					var destroyDuration = clock(destroyStart);
					console.log('Removing ' + docCount + ' documents took ' + destroyDuration + 'ms (noTrigger = false)');
					_next();
				} else {
					if (removeIndex % 100 == 0) setTimeout(removeOne, 0);
					else removeOne();
				}
			}

			removeOne();
		}
	}

	function checkDestruction(_next){
		if (!testIndex) throw new Error('testIndex must be defined');

		console.log('Checking the non-existence of ' + docCount + ' documents');

		var lookupStart = clock();

		var lookupIndex = 0;

		function lookupOne(){
			var currentTuple = dataSet[lookupIndex];
			testIndex.lookup(currentTuple.k, function(err, value){
				if (err) throw err;

				assert(!value);

				nextLookup();
			});
		}

		function nextLookup(){
			lookupIndex++;
			if (lookupIndex == docCount){
				var lookupDuration = clock(lookupStart);
				console.log('Looking up ' + docCount + ' non-existing docs took ' + lookupDuration + 'ms');
				_next();
			} else {
				if (lookupIndex % 100 == 0) setTimeout(lookupOne, 0);
				else lookupOne();
			}
		}

		lookupOne();
	}

	saveIndex(function(){
		loadIndex(function(){
			destroyIndex(function(){
				checkDestruction(cb);
			});
		});
	});
}

console.log('----------------------');
console.log('Basic index testing');
console.log('----------------------');
basicTests(function(){
	console.log('done');
	console.log('');
	console.log('----------------------');
	console.log('Data load index testing');
	console.log('----------------------');
	var st1 = clock();
	loadTests(undefined, function(){
		var duration = clock(st1);
		console.log('done in ' + duration.toString() + 'ms');

		console.log('');
		console.log('----------------------');
		console.log('Data load index testing (noTrigger == true)');
		console.log('----------------------');
		var st2 = clock();
		loadTests(undefined, function(){
			var duration = clock(st2);
			console.log('done in ' + duration.toString() + 'ms');

			console.log('');
			console.log('----------------------');
			console.log('Bigger load index testing');
			console.log('----------------------');
			var st3 = clock();
			loadTests(100000, function(){
				var duration = clock(st3);
				console.log('done in ' + duration.toString() + 'ms');

				console.log('');
				console.log('----------------------');
				console.log('Mega load index testing (500k docs)');
				console.log('----------------------');
				var st4 = clock();
				loadTests(500000, function(){
					var duration = clock(st4);
					console.log('done in ' + duration.toString() + 'ms');
				}, true);
			}, true);
		}, true);
	});
});
<commit_msg>"Mega test" not part of default testing<commit_after>var fs = require('fs');
var path = require('path')
var assert = require('assert');
var Lawncipher = require('../');
var Long = require('long');
var faker = require('faker');

var runMega = process.argv.length > 2 && process.argv[2] == 'mega';

var rmdir = require('rmdir');

var sodium = require('libsodium-wrappers');
var to_hex = sodium.to_hex, from_hex = sodium.from_hex;

var deepObjectEquality = Lawncipher.deepObjectEquality;
var randomBuffer = Lawncipher.randomBuffer;

var PearsonBPlusTree = Lawncipher.PearsonBPlusTree;
var PearsonSeedGenerator = Lawncipher.PearsonSeedGenerator;
var PearsonHasher = Lawncipher.PearsonHasher;
var Index = Lawncipher.Index;

var testIndexPath = path.join(__dirname, 'test_index');

var testSeed = [
	98,  6, 85, 150, 36, 23, 112, 164, 135, 207, 169,  5, 26, 64, 165, 219,
	61, 20, 68, 89, 130, 63, 52, 102, 24, 229, 132, 245, 80, 216, 195, 115,
	90, 168, 156, 203, 177, 120,  2, 190, 188,  7, 100, 185, 174, 243, 162, 10,
	237, 18, 253, 225, 8, 208, 172, 244, 255, 126, 101, 79, 145, 235, 228, 121,
	123, 251, 67, 250, 161, 0, 107, 97, 241, 111, 181, 82, 249, 33, 69, 55,
	59,153, 29, 9, 213, 167, 84, 93, 30, 46, 94, 75, 151, 114, 73, 222,
	197, 96, 210, 45, 16, 227, 248, 202, 51, 152, 252, 125, 81, 206, 215, 186,
	39, 158, 178, 187, 131, 136,  1, 49, 50, 17, 141, 91, 47, 129, 60, 99,
	154, 35, 86, 171, 105, 34, 38, 200, 147, 58, 77, 118, 173, 246, 76, 254,
	133, 232, 196, 144, 198, 124, 53, 4, 108, 74, 223, 234, 134, 230, 157, 139,
	189, 205, 199, 128, 176, 19, 211, 236, 127, 192, 231, 70, 233, 88, 146, 44,
	183, 201, 22, 83, 13, 214, 116, 109, 159, 32, 95, 226, 140, 220, 57, 12,
	221, 31, 209, 182, 143, 92, 149, 184, 148, 62, 113, 65, 37, 27, 106, 166,
	3, 14, 204, 72, 21, 41, 56, 66, 28, 193, 40, 217, 25, 54,179,117,
	238, 87, 240, 155, 180, 170, 242, 212, 191, 163, 78, 218, 137, 194, 175, 110,
	43, 119, 224, 71, 122, 142, 42, 160, 104, 48, 247, 103, 15, 11, 138, 239
];

function arrayEquality(a1, a2){
	if (!(Array.isArray(a1) && Array.isArray(a2))) return false;
	if (a1.length != a2.length) return false;

	for (var i = 0; i < a1.length; i++){
		if (a1[i] != a2[i]) return false;
	}
	return true;
}

function newRandomUserDoc(){
	return {
		name: faker.name.firstName() + ' ' + faker.name.lastName(),
		email: faker.internet.email(),
		address: faker.address.streetName() + '\n' + faker.address.city() + '\n' + faker.address.country()
	};
}

function clock(s){
	if (!s) return process.hrtime();
	var d = process.hrtime(s);
	return Math.round(d[0] * 1000 + d[1] / 1000000);
}

function basicTests(next){
	var indexKey = randomBuffer(32);

	function saveIndex(_next){
		rmdir(testIndexPath, function(err){
			if (err) throw err;

			var testIndex = new Index(__dirname, 'test_index', 'index', indexKey, testSeed, function(loadErr){
				if (loadErr) throw loadErr;

				testIndex.add('test', 1, function(e){
					if (e) throw e;

					testIndex.add('hello', 2, function(e){
						if (e) throw e;

						_next();
					});
				});
			});
		});
	}

	function loadIndex(_next){
		var testIndex = new Index(__dirname, 'test_index', 'index', indexKey, testSeed, function(loadErr){
			if (loadErr) throw loadErr;

			testIndex.lookup('test', function(err, value){
				if (err) throw err;

				assert(value == 1);

				testIndex.lookup('hello', function(err, value){
					if (err) throw err;

					assert(value == 2);

					_next();
				});
			});
		});
	}

	saveIndex(function(){
		loadIndex(next);
	});
}

//A testing method, designed to test save, loading, lookup and removal of a non-negligeable data set
function loadTests(docCount, cb, usingNoTrigger){
	docCount = docCount || 1000;
	if (typeof cb != 'function') throw new TypeError('cb must be a function');

	var testIndex;
	var indexKey = randomBuffer(32);
	var indexSeed = PearsonSeedGenerator();

	var dataSet = new Array(docCount);

	console.log('Generating ' + docCount + ' documents');

	for (var i = 0; i < docCount; i++){
		dataSet[i] = {k: faker.random.uuid(), v: newRandomUserDoc()};
	}

	function saveIndex(_next){
		rmdir(testIndexPath, function(err){
			if (err) throw err;

			console.log('Starting index writes');
			var saveStart = clock();
			testIndex = new Index(__dirname, 'test_index', 'index', indexKey, indexSeed, function(loadErr){
				if (loadErr) throw loadErr;

				console.log('Saving ' + docCount + ' documents' + (usingNoTrigger ? ' (while using noTrigger = true)' : ''));

				if (usingNoTrigger){
					for (var i = 0; i < docCount - 1; i++){
						testIndex.add(dataSet[i].k, dataSet[i].v, undefined, true);
					}

					testIndex.add(dataSet[dataSet.length - 1].k, dataSet[dataSet.length - 1].v, function(err){
						if (err) throw err;

						var saveDuration = clock(saveStart);
						console.log('Saving ' + docCount + ' documents took ' + saveDuration + 'ms (noTrigger = true)');
						_next();
					});
				} else {
					//usingNoTrigger == false -> async everything

					var addIndex = 0;

					function addOne(){
						var currentTuple = dataSet[addIndex];
						testIndex.add(currentTuple.k, currentTuple.v, function(err){
							if (err) throw err;

							nextAdd();
						});
					}

					function nextAdd(){
						addIndex++;
						if (addIndex == docCount){
							var saveDuration = clock(saveStart);
							console.log('Saving ' + docCount + ' documents took ' + saveDuration + 'ms (noTrigger = false)')
							_next();
						} else {
							if (addIndex % 100 == 0) setTimeout(addOne, 0);
							else addOne();
						}
					}

					addOne();
				}
			});
		});
	}

	function loadIndex(_next){
		var loadStart = clock();
		testIndex = new Index(__dirname, 'test_index', 'index', indexKey, indexSeed, function(loadErr){
			if (loadErr) throw loadErr;

			console.log('Loading and looking up ' + docCount + ' documents');

			var lookupIndex = 0;

			function lookupOne(){
				var currentTuple = dataSet[lookupIndex];
				testIndex.lookup(currentTuple.k, function(err, value){
					if (err) throw err;

					//console.log('lookupIndex: ' + lookupIndex);
					//console.log('Found value: ' + JSON.stringify(value));
					//console.log('Expected value: ' + JSON.stringify(currentTuple.v));
					assert(deepObjectEquality(currentTuple.v, value));

					nextLookup();
				});
			}

			function nextLookup(){
				lookupIndex++;
				if (lookupIndex == docCount){
					var loadDuration = clock(loadStart);
					console.log('Loading and looking up ' + docCount + ' documents took ' + loadDuration + 'ms');
					_next();
				} else {
					if (lookupIndex % 100 == 0) setTimeout(lookupOne, 0);
					else lookupOne();
				}
			}

			lookupOne();
		});
	}

	function destroyIndex(_next){
		if (!testIndex) throw new Error('testIndex must be defined');

		console.log('Removing ' + docCount + ' documents');

		var destroyStart = clock();

		if (usingNoTrigger){
			for (var i = 0; i < docCount - 1; i++){
				testIndex.remove(dataSet[i].k, undefined, undefined, true);
			}

			testIndex.remove(dataSet[docCount - 1].k, undefined, function(err){
				if (err) throw err;

				var destroyDuration = clock(destroyStart);
				console.log('Removing ' + docCount + ' documents took ' + destroyDuration + 'ms (noTrigger = true)');
				_next();
			});

		} else {
			var removeIndex = 0;

			function removeOne(){
				var currentTuple = dataSet[removeIndex];
				testIndex.remove(currentTuple.k, undefined, function(err){
					if (err) throw err;

					nextRemoval();
				}, usingNoTrigger && removeIndex < docCount - 1);
			}

			function nextRemoval(){
				removeIndex++;
				if (removeIndex == docCount){
					var destroyDuration = clock(destroyStart);
					console.log('Removing ' + docCount + ' documents took ' + destroyDuration + 'ms (noTrigger = false)');
					_next();
				} else {
					if (removeIndex % 100 == 0) setTimeout(removeOne, 0);
					else removeOne();
				}
			}

			removeOne();
		}
	}

	function checkDestruction(_next){
		if (!testIndex) throw new Error('testIndex must be defined');

		console.log('Checking the non-existence of ' + docCount + ' documents');

		var lookupStart = clock();

		var lookupIndex = 0;

		function lookupOne(){
			var currentTuple = dataSet[lookupIndex];
			testIndex.lookup(currentTuple.k, function(err, value){
				if (err) throw err;

				assert(!value);

				nextLookup();
			});
		}

		function nextLookup(){
			lookupIndex++;
			if (lookupIndex == docCount){
				var lookupDuration = clock(lookupStart);
				console.log('Looking up ' + docCount + ' non-existing docs took ' + lookupDuration + 'ms');
				_next();
			} else {
				if (lookupIndex % 100 == 0) setTimeout(lookupOne, 0);
				else lookupOne();
			}
		}

		lookupOne();
	}

	saveIndex(function(){
		loadIndex(function(){
			destroyIndex(function(){
				checkDestruction(cb);
			});
		});
	});
}

console.log('----------------------');
console.log('Basic index testing');
console.log('----------------------');
basicTests(function(){
	console.log('done');
	console.log('');
	console.log('----------------------');
	console.log('Data load index testing');
	console.log('----------------------');
	var st1 = clock();
	loadTests(undefined, function(){
		var duration = clock(st1);
		console.log('done in ' + duration.toString() + 'ms');

		console.log('');
		console.log('----------------------');
		console.log('Data load index testing (noTrigger == true)');
		console.log('----------------------');
		var st2 = clock();
		loadTests(undefined, function(){
			var duration = clock(st2);
			console.log('done in ' + duration.toString() + 'ms');

			console.log('');
			console.log('----------------------');
			console.log('Bigger load index testing');
			console.log('----------------------');
			var st3 = clock();
			loadTests(100000, function(){
				var duration = clock(st3);
				console.log('done in ' + duration.toString() + 'ms');

				if (!runMega) return;
				console.log('');
				console.log('----------------------');
				console.log('Mega load index testing (500k docs)');
				console.log('----------------------');
				var st4 = clock();
				loadTests(500000, function(){
					var duration = clock(st4);
					console.log('done in ' + duration.toString() + 'ms');
				}, true);
			}, true);
		}, true);
	});
});
<|endoftext|>