<commit_before>// Copyright 2016 David Lavieri.  All rights reserved.
// Use of this source code is governed by a MIT License
// License that can be found in the LICENSE file.

jest.unmock('./../Dropdown')

var jsdom = require('jsdom').jsdom

global.document = jsdom('<!doctype html>')
global.window = document.defaultView
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    global[property] = document.defaultView[property]
  }
})

global.navigator = {
  userAgent: 'node.js'
}

import React from 'react'
import {shallow, mount} from 'enzyme'
import sinon from 'sinon'

import {Dropdown} from './../Dropdown'

describe('Menu.Dropdown', () => {
  const commonContent = [
SingleItem
]
  const commonHeader = [, Header]
  const commonCB = () => {}

  it('should not be open (mount)', () => {
    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('open')
    ).toBeFalsy()
  })

  it('should be open prop provided', () => {
    const wrapper = shallow(
      <Dropdown
        open
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('open')
    ).toBeTruthy()

    wrapper.setProps({open: false})

    expect(
      wrapper.hasClass('open')
    ).toBeFalsy()
  })

  it('should add classnames', () => {
    const wrapper = shallow(
      <Dropdown
        cn='notifications'
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('notifications')
    ).toBeTruthy()
  })

  it('should pass down children', () => {
    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.find('a').at(0).contains(commonHeader)
    ).toBeTruthy()

    expect(
      wrapper.find('.dropdown-menu').contains(commonContent)
    ).toBeTruthy()
  })

  it('should call toggle function on click', () => {
    const onToggle = sinon.spy()

    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={onToggle} />
    )

    expect(
      wrapper.instance().props.open
    ).toEqual(undefined)

    wrapper.find('.dropdown-toggle').simulate('click')

    expect(
      onToggle.callCount
    ).toEqual(1)

    expect(
      onToggle.calledWith(true)
    ).toBeTruthy()
  })

  it('should call focus method on focus (menu)', () => {
    const onFocus = sinon.spy()

    const wrapper = mount(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB}
        onFocus={onFocus}
         />
     )

    wrapper.find('.dropdown-menu').simulate('focus')

    expect(
    onFocus.called
    ).toBeTruthy()
  })

  it('should call toggle function on blur', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('blur')

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(
          expect(
            onToggle.called
          ).toBeTruthy()
        )

        wrapper.unmount()
      })
    })
  })

  it('should call toggle function on blur argument false', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('blur')

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(
          expect(
            onToggle.calledWith(false)
          ).toBeTruthy()
        )

        wrapper.unmount()
      })
    })
  })

  it('should not call onToggle function on unmount if closed', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.unmount()

    expect(
      onToggle.called
    ).toBeFalsy()
  })

  it('should call onToggle function on unmount if open', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          open
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.unmount()

    expect(
      onToggle.called
    ).toBeTruthy()

    expect(
      onToggle.calledWith(false)
    ).toBeTruthy()
  })

  it('should not call toggle function on keydown (esc)', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('keyDown', {keyCode: 27})

    expect(
      onToggle.called
    ).toBeFalsy()

    wrapper.unmount()
  })

  it('should not call toggle function on keydown (esc) if open', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          open
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('keyDown', {keyCode: 27})

    expect(
      onToggle.called
    ).toBeTruthy()

    expect(
      onToggle.calledWith(false)
    ).toBeTruthy()

    wrapper.unmount()
  })
})
<commit_msg>Add cover test case Dropdown Component<commit_after>// Copyright 2016 David Lavieri.  All rights reserved.
// Use of this source code is governed by a MIT License
// License that can be found in the LICENSE file.

jest.unmock('./../Dropdown')

var jsdom = require('jsdom').jsdom

global.document = jsdom('<!doctype html>')
global.window = document.defaultView
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    global[property] = document.defaultView[property]
  }
})

global.navigator = {
  userAgent: 'node.js'
}

import React from 'react'
import {shallow, mount} from 'enzyme'
import sinon from 'sinon'

import {Dropdown} from './../Dropdown'

describe('Menu.Dropdown', () => {
  const commonContent = [
SingleItem
]
  const commonHeader = [, Header]
  const commonCB = () => {}

  it('should not be open (mount)', () => {
    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('open')
    ).toBeFalsy()
  })

  it('should be open prop provided', () => {
    const wrapper = shallow(
      <Dropdown
        open
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('open')
    ).toBeTruthy()

    wrapper.setProps({open: false})

    expect(
      wrapper.hasClass('open')
    ).toBeFalsy()
  })

  it('should add classnames', () => {
    const wrapper = shallow(
      <Dropdown
        cn='notifications'
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.hasClass('notifications')
    ).toBeTruthy()
  })

  it('should pass down children', () => {
    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB} />
    )

    expect(
      wrapper.find('a').at(0).contains(commonHeader)
    ).toBeTruthy()

    expect(
      wrapper.find('.dropdown-menu').contains(commonContent)
    ).toBeTruthy()
  })

  it('should call toggle function on click', () => {
    const onToggle = sinon.spy()

    const wrapper = shallow(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={onToggle} />
    )

    expect(
      wrapper.instance().props.open
    ).toEqual(undefined)

    wrapper.find('.dropdown-toggle').simulate('click')

    expect(
      onToggle.callCount
    ).toEqual(1)

    expect(
      onToggle.calledWith(true)
    ).toBeTruthy()

    wrapper.setProps({open: true})

    wrapper.find('.dropdown-toggle').simulate('click')

    expect(
      onToggle.callCount
    ).toEqual(2)

    expect(
      onToggle.calledWith(false)
    ).toBeTruthy()
  })

  it('should call focus method on focus (menu)', () => {
    const onFocus = sinon.spy()

    const wrapper = mount(
      <Dropdown
        header={commonHeader}
        content={commonContent}
        onToggle={commonCB}
        onFocus={onFocus}
         />
     )

    wrapper.find('.dropdown-menu').simulate('focus')

    expect(
    onFocus.called
    ).toBeTruthy()
  })

  it('should call toggle function on blur', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('blur')

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(
          expect(
            onToggle.called
          ).toBeTruthy()
        )

        wrapper.unmount()
      })
    })
  })

  it('should call toggle function on blur argument false', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('blur')

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(
          expect(
            onToggle.calledWith(false)
          ).toBeTruthy()
        )

        wrapper.unmount()
      })
    })
  })

  it('should not call onToggle function on unmount if closed', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.unmount()

    expect(
      onToggle.called
    ).toBeFalsy()
  })

  it('should call onToggle function on unmount if open', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          open
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.unmount()

    expect(
      onToggle.called
    ).toBeTruthy()

    expect(
      onToggle.calledWith(false)
    ).toBeTruthy()
  })

  it('should not call toggle function on keydown (esc)', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('keyDown', {keyCode: 27})

    expect(
      onToggle.called
    ).toBeFalsy()

    wrapper.unmount()
  })

  it('should not call toggle function on keydown (esc) if open', () => {
    const onToggle = sinon.spy()

    const wrapper = mount(

        <Dropdown
          open
          header={commonHeader}
          content={commonContent}
          onToggle={onToggle} />

    )

    wrapper.find('.dropdown-menu').simulate('keyDown', {keyCode: 27})

    expect(
      onToggle.called
    ).toBeTruthy()

    expect(
      onToggle.calledWith(false)
    ).toBeTruthy()

    wrapper.unmount()
  })
})
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
// selector
import { selectSteps } from './selectors';

// Import Components
import { Tree, Modal } from 'antd';
import taskstep from './TaskSteps';
// CSS
import 'antd/dist/antd.css';
import styles from './styles.css';

// Import Actions
import { updateTasks } from '../../TrajVAActions';

const TreeNode = Tree.TreeNode;

class TaskPanel extends Component {
	constructor(props){
		super(props);
		this.onCheck = this.onCheck.bind(this);
		this.state = {
			selectedNode: null,
			overviewTrData: [ { name: "基站分布",key: "0-0-0-0" }, { name: "人群特征", key: "0-0-0-1" } ],
			analysisTrData: [ { name: "出行模式分析",key: "0-0-1-0" } ],
			concolusionTrD: [ { name: "人群出行模式",key: "0-0-2-0" } ],
		};
	}

	onSelect = (selectedKeys, info) => {
		console.log('selected', selectedKeys, info);
	}

	onCheck = (checkedKeys, info) => {
		console.log('onCheck', checkedKeys, info);
		const checkedNodePos = info.checkedNodesPositions.map( e => e.pos);
		const steps = this.props.steps.map((item,index) => {
			if (info.halfCheckedKeys.includes(`0-0-${index}`)){
				item.status = "process";
			}	else if (checkedNodePos.includes(`0-0-${index}`)){
				item.status = "finish";
			} else {
				item.status = "wait";
			}
			return item;
		});
		if(checkedNodePos.includes('0-0')){
			steps[3].status = "finish";
		} else {
			steps[3].status = "wait";
		}
		this.props.updateTasksState({steps});
	}

	onRightClick = (e) => {

		// document.oncontextmenu = () => false;
		const oEvent = e.event;
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='block';
		oDiv.style.left = oEvent.clientX + 'px';
		oDiv.style.top = oEvent.clientY + 'px';
		if (oEvent.preventDefault) oEvent.preventDefault();

		const onremovehandle = () => {
			 oDiv.style.display = 'none';
			 window.removeEventListener('click', onremovehandle);
		}
		window.addEventListener('click', onremovehandle);

		this.setState({selectedNode:e.node});
	}

	taskmenuClick = (e) => {
		console.log(e);
	}

	menuClick = (e) => {
		console.log(e);
		if(e.target.tagName === 'LI') {
			switch(e.target.value) {
				case 0: this.rename(e);break;
				case 1: this.addNewNode(e);break;
				case 2: this.removeNode(e);break;
				default: break;
			}
		}
	}

	rename = (e) => {

		// hide contextmenu
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const element = this.state.selectedNode.refs.selectHandle;
		const name = element.text;

		const oldInnerHTML = element.innerHTML;
		element.innerHTML = `<input id="input1" type="text" value=${name}>`;
		const inputbox = document.getElementById('input1');
		inputbox.style.offsetTop = element.offsetTop;
		inputbox.style.offsetLeft = element.offsetLeft;
		inputbox.style.offsetWidth = element.offsetWidth;
		inputbox.style.offsetHeight = element.offsetHeight;

		inputbox.focus();

		inputbox.addEventListener("blur", ( event ) => {
			const newName = event.target.value;

			event.target.parentNode.removeChild(event.target);

			// find key node
			const overviewTrData = this.state.overviewTrData;
			let node = overviewTrData.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					overviewTrData: overviewTrData.map(e => e)
				});
				return;
			}

			const analysisTrData = this.state.analysisTrData;
			node = analysisTrData.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					analysisTrData: analysisTrData.map(e => e)
				});
				return;
			}

			const concolusionTrD = this.state.concolusionTrD;
			node = concolusionTrD.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					concolusionTrD: concolusionTrD.map(e => e)
				});
				return;
			}

			element.innerHTML = oldInnerHTML;

		}, true);

	}

	addNewNode = (e) => {
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const overviewTrData = this.state.overviewTrData.map(e => e);
		const analysisTrData = this.state.analysisTrData.map(e => e);
		const concolusionTrD =  this.state.concolusionTrD.map(e => e);
		let node = overviewTrData.find((item) => item.key === key);
		let indexTr = 0;
		if (!node) {
			node = analysisTrData.find((item) => item.key === key);
			indexTr = 1
			if (!node) {
				node = concolusionTrD.find((item) => item.key === key);
				indexTr = 2;
			}
		}

		if (node) {
			const newIndex = +key.charAt(key.length-1) + 1;
			const newKey = key.substr(0, key.length-1) + newIndex;
			const newNode = { name: '新节点', key: newKey };
			switch(indexTr) {
				case 0:
					overviewTrData.push(newNode);
					this.setState({ overviewTrData });
					break;
				case 1:
					analysisTrData.push(newNode);
					this.setState({ analysisTrData });
					break;
				case 2:
					concolusionTrD.push(newNode);
					this.setState({ concolusionTrD });
					break;
				default: break;
			}
		}

	}

	removeNode = (e) => {
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const overviewTrData = this.state.overviewTrData.map(e => e);
		const analysisTrData = this.state.analysisTrData.map(e => e);
		const concolusionTrD =  this.state.concolusionTrD.map(e => e);
		let node = overviewTrData.findIndex((item) => item.key === key);
		let indexTr = 0;
		if (node === -1) {
			node = analysisTrData.findIndex((item) => item.key === key);
			indexTr = 1
			if (node === -1) {
				node = concolusionTrD.findIndex((item) => item.key === key);
				indexTr = 2;
			}
		}

		if (node !== -1) {

			Modal.confirm({
				title: '删除节点',
				content: (

是否确认删除节点？

				),
				// onOk
				onOk: () => {
					switch(indexTr) {
						case 0:
							overviewTrData.splice(node,1);
							this.setState({ overviewTrData });
							break;
						case 1:
							analysisTrData.splice(node,1);
							this.setState({ analysisTrData });
							break;
						case 2:
							concolusionTrD.splice(node,1);
							this.setState({ concolusionTrD });
							break;
						default: break;
					}
				},
				// onCancel
				onCancel: () => {

				}
			});
		}
	}

	generateTreeNode = (data) => {
		return data.map((item, index) => (<TreeNode title={item.name} key={item.key}/>));
	}

	render() {
		return (

				<Tree
					checkable
					defaultExpandedKeys={['0-0-0', '0-0-1']}
					defaultSelectedKeys={['0-0-0', '0-0-1']}
					defaultCheckedKeys={['0-0-0', '0-0-1']}
					onSelect={this.onSelect}
					onCheck={this.onCheck}
					onRightClick={this.onRightClick}
				>
					<TreeNode title="任务阶段" key="0-0">
						<TreeNode title="概览" key="0-0-0">
							{ [...this.generateTreeNode(this.state.overviewTrData)] }
						</TreeNode>
						<TreeNode title="分析" key="0-0-1">
							 { [...this.generateTreeNode(this.state.analysisTrData)] }
						</TreeNode>
						<TreeNode title="结论" key="0-0-2">
							 { [...this.generateTreeNode(this.state.concolusionTrD)] }
						</TreeNode>
					</TreeNode>
				</Tree>

重命名

添加

删除

		);
	}
}

TaskPanel.propTypes = {
	ss: PropTypes.func,
	steps: PropTypes.array.isRequired
};

// Retrieve data from store as props
const mapStateToProps = createStructuredSelector({
	steps: selectSteps
});

export function mapDispatchToProps(dispatch) {
	return {
		changeRoute: (url) => dispatch(push(url)),
		updateTasksState: (taskData) => dispatch(updateTasks(taskData))
	};
}

export default connect(mapStateToProps, mapDispatchToProps)(TaskPanel);
<commit_msg>fix rename error<commit_after>import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
// selector
import { selectSteps } from './selectors';

// Import Components
import { Tree, Modal } from 'antd';
import taskstep from './TaskSteps';
// CSS
import 'antd/dist/antd.css';
import styles from './styles.css';

// Import Actions
import { updateTasks } from '../../TrajVAActions';

const TreeNode = Tree.TreeNode;

class TaskPanel extends Component {
	constructor(props){
		super(props);
		this.onCheck = this.onCheck.bind(this);
		this.state = {
			selectedNode: null,
			overviewTrData: [ { name: "基站分布",key: "0-0-0-0" }, { name: "人群特征", key: "0-0-0-1" } ],
			analysisTrData: [ { name: "出行模式分析",key: "0-0-1-0" } ],
			concolusionTrD: [ { name: "人群出行模式",key: "0-0-2-0" } ],
		};
	}

	onSelect = (selectedKeys, info) => {
		console.log('selected', selectedKeys, info);
	}

	onCheck = (checkedKeys, info) => {
		console.log('onCheck', checkedKeys, info);
		const checkedNodePos = info.checkedNodesPositions.map( e => e.pos);
		const steps = this.props.steps.map((item,index) => {
			if (info.halfCheckedKeys.includes(`0-0-${index}`)){
				item.status = "process";
			}	else if (checkedNodePos.includes(`0-0-${index}`)){
				item.status = "finish";
			} else {
				item.status = "wait";
			}
			return item;
		});
		if(checkedNodePos.includes('0-0')){
			steps[3].status = "finish";
		} else {
			steps[3].status = "wait";
		}
		this.props.updateTasksState({steps});
	}

	onRightClick = (e) => {

		// document.oncontextmenu = () => false;
		const oEvent = e.event;
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='block';
		oDiv.style.left = oEvent.clientX + 'px';
		oDiv.style.top = oEvent.clientY + 'px';
		if (oEvent.preventDefault) oEvent.preventDefault();

		const onremovehandle = () => {
			 oDiv.style.display = 'none';
			 window.removeEventListener('click', onremovehandle);
		}
		window.addEventListener('click', onremovehandle);

		this.setState({selectedNode:e.node});
	}

	taskmenuClick = (e) => {
		console.log(e);
	}

	menuClick = (e) => {
		console.log(e);
		if(e.target.tagName === 'LI') {
			switch(e.target.value) {
				case 0: this.rename(e);break;
				case 1: this.addNewNode(e);break;
				case 2: this.removeNode(e);break;
				default: break;
			}
		}
	}

	rename = (e) => {

		// hide contextmenu
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const element = this.state.selectedNode.refs.selectHandle;
		const name = element.text;

		const oldInnerHTML = element.innerHTML;
		element.innerHTML = `<input id="input1" type="text" value=${name}>`;
		const inputbox = document.getElementById('input1');
		inputbox.style.offsetTop = element.offsetTop;
		inputbox.style.offsetLeft = element.offsetLeft;
		inputbox.style.offsetWidth = element.offsetWidth;
		inputbox.style.offsetHeight = element.offsetHeight;

		inputbox.focus();

		inputbox.addEventListener("blur", ( event ) => {
			const newName = event.target.value;

			event.target.parentNode.removeChild(event.target);

			element.innerHTML = oldInnerHTML;

			element.text = newName;

			// find key node
			const overviewTrData = this.state.overviewTrData;
			let node = overviewTrData.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					overviewTrData: overviewTrData.map(e => e)
				});
				return;
			}

			const analysisTrData = this.state.analysisTrData;
			node = analysisTrData.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					analysisTrData: analysisTrData.map(e => e)
				});
				return;
			}

			const concolusionTrD = this.state.concolusionTrD;
			node = concolusionTrD.find((item) => item.key === key);
			if (node) {
				node.name = newName;
				this.setState({
					concolusionTrD: concolusionTrD.map(e => e)
				});
				return;
			}

		}, true);

	}

	addNewNode = (e) => {
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const overviewTrData = this.state.overviewTrData.map(e => e);
		const analysisTrData = this.state.analysisTrData.map(e => e);
		const concolusionTrD =  this.state.concolusionTrD.map(e => e);
		let node = overviewTrData.find((item) => item.key === key);
		let indexTr = 0;
		if (!node) {
			node = analysisTrData.find((item) => item.key === key);
			indexTr = 1
			if (!node) {
				node = concolusionTrD.find((item) => item.key === key);
				indexTr = 2;
			}
		}

		if (node) {
			const newIndex = +key.charAt(key.length-1) + 1;
			const newKey = key.substr(0, key.length-1) + newIndex;
			const newNode = { name: '新节点', key: newKey };
			switch(indexTr) {
				case 0:
					overviewTrData.push(newNode);
					this.setState({ overviewTrData });
					break;
				case 1:
					analysisTrData.push(newNode);
					this.setState({ analysisTrData });
					break;
				case 2:
					concolusionTrD.push(newNode);
					this.setState({ concolusionTrD });
					break;
				default: break;
			}
		}

	}

	removeNode = (e) => {
		const oDiv = document.getElementById('tasktreemenu');
		oDiv.style.display ='none';

		const key = this.state.selectedNode.props.eventKey;

		const overviewTrData = this.state.overviewTrData.map(e => e);
		const analysisTrData = this.state.analysisTrData.map(e => e);
		const concolusionTrD =  this.state.concolusionTrD.map(e => e);
		let node = overviewTrData.findIndex((item) => item.key === key);
		let indexTr = 0;
		if (node === -1) {
			node = analysisTrData.findIndex((item) => item.key === key);
			indexTr = 1
			if (node === -1) {
				node = concolusionTrD.findIndex((item) => item.key === key);
				indexTr = 2;
			}
		}

		if (node !== -1) {

			Modal.confirm({
				title: '删除节点',
				content: (

是否确认删除节点？

				),
				// onOk
				onOk: () => {
					switch(indexTr) {
						case 0:
							overviewTrData.splice(node,1);
							this.setState({ overviewTrData });
							break;
						case 1:
							analysisTrData.splice(node,1);
							this.setState({ analysisTrData });
							break;
						case 2:
							concolusionTrD.splice(node,1);
							this.setState({ concolusionTrD });
							break;
						default: break;
					}
				},
				// onCancel
				onCancel: () => {

				}
			});
		}
	}

	generateTreeNode = (data) => {
		return data.map((item, index) => (<TreeNode title={item.name} key={item.key}/>));
	}

	render() {
		return (

				<Tree
					checkable
					defaultExpandedKeys={['0-0-0', '0-0-1']}
					defaultSelectedKeys={['0-0-0', '0-0-1']}
					defaultCheckedKeys={['0-0-0', '0-0-1']}
					onSelect={this.onSelect}
					onCheck={this.onCheck}
					onRightClick={this.onRightClick}
				>
					<TreeNode title="任务阶段" key="0-0">
						<TreeNode title="概览" key="0-0-0">
							{ [...this.generateTreeNode(this.state.overviewTrData)] }
						</TreeNode>
						<TreeNode title="分析" key="0-0-1">
							 { [...this.generateTreeNode(this.state.analysisTrData)] }
						</TreeNode>
						<TreeNode title="结论" key="0-0-2">
							 { [...this.generateTreeNode(this.state.concolusionTrD)] }
						</TreeNode>
					</TreeNode>
				</Tree>

重命名

添加

删除

		);
	}
}

TaskPanel.propTypes = {
	ss: PropTypes.func,
	steps: PropTypes.array.isRequired
};

// Retrieve data from store as props
const mapStateToProps = createStructuredSelector({
	steps: selectSteps
});

export function mapDispatchToProps(dispatch) {
	return {
		changeRoute: (url) => dispatch(push(url)),
		updateTasksState: (taskData) => dispatch(updateTasks(taskData))
	};
}

export default connect(mapStateToProps, mapDispatchToProps)(TaskPanel);
<|endoftext|>


<commit_before>'use strict';

goog.provide('Blockly.Python.procedures');

goog.require('Blockly.Python');

Blockly.Python.procedures_defreturn = function() {
  // Define a procedure with a return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var branch = Blockly.Python.statementToCode(this, 'STACK');
  if (Blockly.Python.INFINITE_LOOP_TRAP) {
    branch = Blockly.Python.INFINITE_LOOP_TRAP.replace(/%1/g,
        '\'' + this.id + '\'') + branch;
  }
  var returnValue = Blockly.Python.valueToCode(this, 'RETURN',
      Blockly.Python.ORDER_NONE) || '';
  //var type=this.getFieldValue('TYPE');
  if (returnValue) {
    returnValue = '    return ' + returnValue + '\n';
  }
  //var returnType = returnValue ? type : 'void';
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
      var varName = Blockly.Python.variableDB_.getName(this.arguments_[x], Blockly.Variables.NAME_TYPE);
    args[x] = varName;
  }
  var code = 'def ' + funcName + '(' + args.join(', ') + '):\n' +
      branch + returnValue + '\n';
  code = Blockly.Python.scrub_(this, code);
  Blockly.Python.setups_[funcName] = code;
  return null;
};

Blockly.Python.procedures_defnoreturn = Blockly.Python.procedures_defreturn;

Blockly.Python.procedures_callreturn = function() {
  // Call a procedure with a return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
    args[x] = Blockly.Python.valueToCode(this, 'ARG' + x,
        Blockly.Python.ORDER_NONE) || 'null';
  }
  var code = funcName + '(' + args.join(', ') + ')';
  return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
};

Blockly.Python.procedures_callnoreturn = function() {
  // Call a procedure with no return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
    args[x] = Blockly.Python.valueToCode(this, 'ARG' + x,
        Blockly.Python.ORDER_NONE) || 'null';
  }
  var code = funcName + '(' + args.join(', ') + ')\n';
  return code;
};

Blockly.Python.procedures_ifreturn = function() {
  // Conditionally return value from a procedure.
  var condition = Blockly.Python.valueToCode(this, 'CONDITION',
      Blockly.Python.ORDER_NONE) || 'False';
  var code = 'if (' + condition + ') :\n';
  if (this.hasReturnValue_) {
    var value = Blockly.Python.valueToCode(this, 'VALUE',
        Blockly.Python.ORDER_NONE) || 'None';
    code += '    return ' + value;
  } else {
    code += '    return None';
  }
  code += '\n';
  return code;
};

Blockly.Python.procedures_return = function() {
  // Conditionally return value from a procedure.
  var code=""
  if (this.hasReturnValue_) {
    var value = Blockly.Python.valueToCode(this, 'VALUE',
        Blockly.Python.ORDER_NONE) || 'None';
    code += 'return ' + value;
  } else {
    code += 'return None';
  }
  code += '\n';
  return code;
};<commit_msg>Update procedures.js<commit_after>'use strict';

goog.provide('Blockly.Python.procedures');

goog.require('Blockly.Python');

Blockly.Python.procedures_defreturn = function() {
  // Define a procedure with a return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var branch = Blockly.Python.statementToCode(this, 'STACK')|| '    pass\n';
  if (Blockly.Python.INFINITE_LOOP_TRAP) {
    branch = Blockly.Python.INFINITE_LOOP_TRAP.replace(/%1/g,
        '\'' + this.id + '\'') + branch;
  }
  var returnValue = Blockly.Python.valueToCode(this, 'RETURN',
      Blockly.Python.ORDER_NONE) || '';
  //var type=this.getFieldValue('TYPE');
  if (returnValue) {
    returnValue = '    return ' + returnValue + '\n';
  }
  //var returnType = returnValue ? type : 'void';
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
      var varName = Blockly.Python.variableDB_.getName(this.arguments_[x], Blockly.Variables.NAME_TYPE);
    args[x] = varName;
  }
  var code = 'def ' + funcName + '(' + args.join(', ') + '):\n' +
      branch + returnValue + '\n';
  code = Blockly.Python.scrub_(this, code);
  Blockly.Python.setups_[funcName] = code;
  return null;
};

Blockly.Python.procedures_defnoreturn = Blockly.Python.procedures_defreturn;

Blockly.Python.procedures_callreturn = function() {
  // Call a procedure with a return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
    args[x] = Blockly.Python.valueToCode(this, 'ARG' + x,
        Blockly.Python.ORDER_NONE) || 'null';
  }
  var code = funcName + '(' + args.join(', ') + ')';
  return [code, Blockly.Python.ORDER_UNARY_POSTFIX];
};

Blockly.Python.procedures_callnoreturn = function() {
  // Call a procedure with no return value.
  var funcName = Blockly.Python.variableDB_.getName(this.getFieldValue('NAME'),
      Blockly.Procedures.NAME_TYPE);
  var args = [];
  for (var x = 0; x < this.arguments_.length; x++) {
    args[x] = Blockly.Python.valueToCode(this, 'ARG' + x,
        Blockly.Python.ORDER_NONE) || 'null';
  }
  var code = funcName + '(' + args.join(', ') + ')\n';
  return code;
};

Blockly.Python.procedures_ifreturn = function() {
  // Conditionally return value from a procedure.
  var condition = Blockly.Python.valueToCode(this, 'CONDITION',
      Blockly.Python.ORDER_NONE) || 'False';
  var code = 'if (' + condition + ') :\n';
  if (this.hasReturnValue_) {
    var value = Blockly.Python.valueToCode(this, 'VALUE',
        Blockly.Python.ORDER_NONE) || 'None';
    code += '    return ' + value;
  } else {
    code += '    return None';
  }
  code += '\n';
  return code;
};

Blockly.Python.procedures_return = function() {
  // Conditionally return value from a procedure.
  var code=""
  if (this.hasReturnValue_) {
    var value = Blockly.Python.valueToCode(this, 'VALUE',
        Blockly.Python.ORDER_NONE) || 'None';
    code += 'return ' + value;
  } else {
    code += 'return None';
  }
  code += '\n';
  return code;
};<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash'),
  filename = __filename.split('/').pop().split('.').shift(),
  lib = require('./' + filename),
  expect = require('chai').expect,
  sinon = require('sinon'),
  files = require('./files'),
  siteService = require('./sites'),
  express = require('express');

describe(_.startCase(filename), function () {
  var sandbox;

  beforeEach(function () {
    sandbox = sinon.sandbox.create();
  });

  afterEach(function () {
    sandbox.restore();
  });

  describe('sortByDepthOfPath', function () {
    var fn = lib[this.title];

    it('greater than', function () {
      expect(fn({path: '1/1'}, {path: '1'})).to.equal(1);
    });

    it('equal', function () {
      expect(fn({path: '01'}, {path: '1'})).to.equal(0);
    });

    it('less than', function () {
      expect(fn({path: '1'}, {path: '1/1'})).to.equal(-1);
    });
  });

  describe('getDefaultSiteSettings', function () {
    var fn = lib[this.title];

    it('www.example.com', function () {
      var hostname = 'www.example.com';
      expect(fn(hostname)).to.deep.equal({
        host: hostname,
        name: 'Example',
        path: '/',
        slug: 'example'
      });
    });
  });

  describe('addSite', function () {
    var fn = lib[this.title];

    function createMockRouter() {
      return {
        use: _.noop,
        all: _.noop,
        get: _.noop,
        put: _.noop,
        post: _.noop
      };
    }

    it('adds controllers', function () {
      var router = createMockRouter(),
        innerRouter = createMockRouter();

      sandbox.stub(express, 'Router', _.constant(innerRouter));
      sandbox.stub(files, 'fileExists');
      sandbox.stub(siteService, 'sites');

      files.fileExists.returns(true);
      siteService.sites.returns({example: {
        slug: 'example',
        assetDir: 'example'
      }});

      // checking for the files to use as controllers shows that we entered the right function.
      // until we add more functionality here, this is good enough.
      sandbox.mock(files).expects('getFiles').returns([]);

      fn(router, {slug: 'example'});

      sandbox.verify();
    });

    it('throws on bad site', function () {
      var router = createMockRouter(),
        innerRouter = createMockRouter();

      sandbox.stub(express, 'Router', _.constant(innerRouter));
      sandbox.stub(files, 'fileExists');
      sandbox.stub(siteService, 'sites');

      files.fileExists.returns(true);
      siteService.sites.returns({example: {
        slug: 'example',
        assetDir: 'example'
      }});

      expect(function () {
        fn(router, {slug: 'badsite'});
      }).to.throw();

    });
  });
});<commit_msg>test to handle case of missing asset folder<commit_after>'use strict';
var _ = require('lodash'),
  filename = __filename.split('/').pop().split('.').shift(),
  lib = require('./' + filename),
  expect = require('chai').expect,
  sinon = require('sinon'),
  files = require('./files'),
  siteService = require('./sites'),
  express = require('express');

describe(_.startCase(filename), function () {
  var sandbox;

  beforeEach(function () {
    sandbox = sinon.sandbox.create();
  });

  afterEach(function () {
    sandbox.restore();
  });

  describe('sortByDepthOfPath', function () {
    var fn = lib[this.title];

    it('greater than', function () {
      expect(fn({path: '1/1'}, {path: '1'})).to.equal(1);
    });

    it('equal', function () {
      expect(fn({path: '01'}, {path: '1'})).to.equal(0);
    });

    it('less than', function () {
      expect(fn({path: '1'}, {path: '1/1'})).to.equal(-1);
    });
  });

  describe('getDefaultSiteSettings', function () {
    var fn = lib[this.title];

    it('www.example.com', function () {
      var hostname = 'www.example.com';
      expect(fn(hostname)).to.deep.equal({
        host: hostname,
        name: 'Example',
        path: '/',
        slug: 'example'
      });
    });
  });

  describe('addSite', function () {
    var fn = lib[this.title];

    function createMockRouter() {
      return {
        use: _.noop,
        all: _.noop,
        get: _.noop,
        put: _.noop,
        post: _.noop
      };
    }

    it('adds controllers', function () {
      var router = createMockRouter(),
        innerRouter = createMockRouter();

      sandbox.stub(express, 'Router', _.constant(innerRouter));
      sandbox.stub(files, 'fileExists');
      sandbox.stub(siteService, 'sites');

      files.fileExists.returns(true);
      siteService.sites.returns({example: {
        slug: 'example',
        assetDir: 'example'
      }});

      // checking for the files to use as controllers shows that we entered the right function.
      // until we add more functionality here, this is good enough.
      sandbox.mock(files).expects('getFiles').returns([]);

      fn(router, {slug: 'example'});

      sandbox.verify();
    });

    it('throws on bad site', function () {
      var router = createMockRouter(),
        innerRouter = createMockRouter();

      sandbox.stub(express, 'Router', _.constant(innerRouter));
      sandbox.stub(files, 'fileExists');
      sandbox.stub(siteService, 'sites');

      files.fileExists.returns(true);
      siteService.sites.returns({example: {
        slug: 'example',
        assetDir: 'example'
      }});

      expect(function () {
        fn(router, {slug: 'badsite'});
      }).to.throw();
    });

    it('throws on missing asset folder', function () {
      var router = createMockRouter(),
        innerRouter = createMockRouter();

      sandbox.stub(express, 'Router', _.constant(innerRouter));
      sandbox.stub(files, 'fileExists');
      sandbox.stub(siteService, 'sites');

      files.fileExists.returns(false);
      siteService.sites.returns({example: {
        slug: 'example',
        assetDir: 'example'
      }});

      expect(function () {
        fn(router, {slug: 'example'});
      }).to.throw();
    });
  });
});<|endoftext|>


<commit_before>module.exports = {
    recipients: 'angelo.angeles@nz.fujitsu.com'
}<commit_msg>added angelo's email to recipients<commit_after>module.exports = {
    recipients: 'angelo.angeles@nz.fujitsu.com, a_m_angeles@yahoo.com'
}
<|endoftext|>


<commit_before>import Emitter from 'helpers/Emitter';
import SoundManager from 'helpers/SoundManager';
import FishGroup from '../FishGroup';

import points from '../Path/paths/path_1.dae';
import createSpline from 'utils/create-spline';
import { loopIndex, degreeToRadian, randomFloat } from 'utils';

import {
  EXP_INTRO_ENDED,
  EXP_RAYCAST_TOGGLE,
  EXP_INTERSECTING_FISH,
  EXP_NOT_INTERSECTING_FISH,
  EXP_SHOW_FISH_NAME,
  EXP_LUX_VALUE_UPDATE,
  EXP_FLASH_MSG
} from 'config/messages';

/**
 * Level class
 */
class Level extends THREE.Object3D {

  /**
   * Constructor function
   * @param {Terrain} Terrain           Terrain instance
   * @param {Camera}  Camera            Camera instance
   * @param {Player}  Player            Player instance
   * @param {Object}  boundingBoxConfig Bounding box configuration
   * @param {Object}  fishGroupConfig   Fish group configuration
   * @param {Object}  resources         Resources
   */
  constructor( Terrain, Camera, Player, boundingBoxConfig, fishGroupConfig, resources ) {

    super();

    this.terrain = Terrain;
    this.camera = Camera;
    this.player = Player;
    this.resources = resources;
    this.hoveredFish = null;
    this.fishGroups = [];
    this.fishModels = [];
    this.intersects = [];

    this.fishGoal = 8;

    this.curve = createSpline( points );
    this.points = this.curve.getSpacedPoints( 100 );

    this.raycastEnabled = false;
    this.wasIntersecting = false;
    this.isIntersecting = false;

    this.intersectingTimeout = null;
    this.intersectingDebounce = false;

    this.raycaster = new THREE.Raycaster();
    this.mouse = new THREE.Vector2();

    fishGroupConfig.map( config => {
      const fishGroup = new FishGroup( config, this.resources, this.curve );
      this.fishGroups.push( fishGroup );

      const geometry = new THREE.Geometry();
      geometry.vertices = this.points;

      const material = new THREE.LineBasicMaterial({ color: false, transparent: true, opacity: 0 });
      const path = new THREE.Line( geometry, material );
      path.position.copy( config.position );
      path.rotation.y = degreeToRadian( 90 );

      for ( let i = 0; i < fishGroup.fishes.length; i++ ) {
        this.fishModels.push( fishGroup.fishes[ i ].modelObject );
      }

      this.add( path );
      path.add( fishGroup );
    });

    this.add( this.terrain );
    this.add( this.player );

    this.norm = 0;

    this.bind();

    Emitter.once( EXP_INTRO_ENDED , this.onIntroEnded );
    Emitter.once( EXP_RAYCAST_TOGGLE , this.toggleRaycast );

  }

  bind() {

    [ 'update', 'onIntroEnded', 'onClick', 'onMouseMove', 'raycast', 'toggleRaycast' ]
        .forEach( ( fn ) => this[ fn ] = this[ fn ].bind( this ) );
  }

  addEventListeners() {

    document.addEventListener( 'click', this.onClick, false );
    document.addEventListener( 'mousemove', this.onMouseMove, false );
  }

  removeEventListeners() {

    document.removeEventListener( 'click', this.onClick, false );
    document.removeEventListener( 'mousemove', this.onMouseMove, false );
  }

  onIntroEnded() {

    this.raycastEnabled = true;

    this.addEventListeners();
  }

  toggleRaycast( toggle ) {
    this.raycastEnabled = toggle;
  }

  onMouseMove( ev ) {

    this.mouse.x = ( ev.clientX / window.innerWidth ) * 2 - 1;
    this.mouse.y = - ( ev.clientY / window.innerHeight ) * 2 + 1;

  }

  onClick() {

    if( this.intersects.length <= 0 ) return;

    const model = this.intersects[ 0 ].object;
    const fish = model.parent.parent;

    const luxGain = randomFloat( 0.05, 0.1 );

    TweenMax.to( model.scale, 1, { x: 0.001, y: 0.001, z: 0.001, ease: Expo.easeOut, onComplete: () => {

      Emitter.emit( EXP_LUX_VALUE_UPDATE, luxGain );

      Emitter.emit( EXP_FLASH_MSG, 'good', `You win + ${luxGain} lux` );

      model.removeFish( fish );
    } });
  }

  updateWindowCursorPointer() {

    if( this.isIntersecting ) {

      document.body.classList.add( 'is-intersecting' );
    } else {

      document.body.classList.remove( 'is-intersecting' );
    }

  }

  raycast() {

    if( this.raycastEnabled ) {

      this.raycaster.setFromCamera( this.mouse, this.camera );

      // calculate objects intersecting the picking ray
      this.intersects = this.raycaster.intersectObjects( this.fishModels );

      this.wasIntersecting = this.isIntersecting;

      if( this.intersects.length > 0 ) {

        // Check distance
        if( this.intersects[ 0 ].distance < 2000 ) {

          this.isIntersecting = true;

          if( this.wasIntersecting !== this.isIntersecting ) {

            // Debounce later system
            clearTimeout( this.intersectingTimeout );

            this.intersectingTimeout = setTimeout( () => {
              this.intersectingDebounce = false;
            }, 300 );

            // Debounce trigger only once per 300ms
            if( !this.intersectingDebounce ) {

              this.intersectingDebounce = true;

              Emitter.emit( EXP_INTERSECTING_FISH, this.intersects[ 0 ] );
              Emitter.emit( EXP_SHOW_FISH_NAME, this.intersects[ 0 ].object.name );

              // Play sound
              SoundManager.play( 'fish-hover' );

              this.hoveredFish = this.intersects[ 0 ].object.parentClass;
              this.hoveredFish.hover();
            }

          }

        }

      } else {

        this.isIntersecting = false;

        if( this.wasIntersecting !== this.isIntersecting ) {

          Emitter.emit( EXP_NOT_INTERSECTING_FISH );

          this.hoveredFish.unhover();
        }
      }

      this.updateWindowCursorPointer();

    }
  }

  update( time ) {

    this.raycast();

    this.norm = loopIndex( this.norm + 0.001, 1 );

    this.fishGroups.map( group => {
      group.update( time );
    });
  }
}

export default Level;
<commit_msg>Experience - Handle fish goal<commit_after>import Emitter from 'helpers/Emitter';
import SoundManager from 'helpers/SoundManager';
import FishGroup from '../FishGroup';

import points from '../Path/paths/path_1.dae';
import createSpline from 'utils/create-spline';
import { loopIndex, degreeToRadian, randomFloat } from 'utils';

import {
  EXP_INTRO_ENDED,
  EXP_RAYCAST_TOGGLE,
  EXP_INTERSECTING_FISH,
  EXP_NOT_INTERSECTING_FISH,
  EXP_SHOW_FISH_NAME,
  EXP_LUX_VALUE_UPDATE,
  EXP_SHOW_VIDEO,
  EXP_FLASH_MSG
} from 'config/messages';

/**
 * Level class
 */
class Level extends THREE.Object3D {

  /**
   * Constructor function
   * @param {Terrain} Terrain           Terrain instance
   * @param {Camera}  Camera            Camera instance
   * @param {Player}  Player            Player instance
   * @param {Object}  boundingBoxConfig Bounding box configuration
   * @param {Object}  fishGroupConfig   Fish group configuration
   * @param {Object}  resources         Resources
   */
  constructor( Terrain, Camera, Player, boundingBoxConfig, fishGroupConfig, resources ) {

    super();

    this.terrain = Terrain;
    this.camera = Camera;
    this.player = Player;
    this.resources = resources;
    this.hoveredFish = null;
    this.fishGroups = [];
    this.fishModels = [];
    this.intersects = [];

    this.fishGoal = 8;
    this.fishCounter = 0;

    this.curve = createSpline( points );
    this.points = this.curve.getSpacedPoints( 100 );

    this.raycastEnabled = false;
    this.wasIntersecting = false;
    this.isIntersecting = false;

    this.intersectingTimeout = null;
    this.intersectingDebounce = false;

    this.raycaster = new THREE.Raycaster();
    this.mouse = new THREE.Vector2();

    fishGroupConfig.map( config => {
      const fishGroup = new FishGroup( config, this.resources, this.curve );
      this.fishGroups.push( fishGroup );

      const geometry = new THREE.Geometry();
      geometry.vertices = this.points;

      const material = new THREE.LineBasicMaterial({ color: false, transparent: true, opacity: 0 });
      const path = new THREE.Line( geometry, material );
      path.position.copy( config.position );
      path.rotation.y = degreeToRadian( 90 );

      for ( let i = 0; i < fishGroup.fishes.length; i++ ) {
        this.fishModels.push( fishGroup.fishes[ i ].modelObject );
      }

      this.add( path );
      path.add( fishGroup );
    });

    this.add( this.terrain );
    this.add( this.player );

    this.norm = 0;

    this.bind();

    Emitter.once( EXP_INTRO_ENDED , this.onIntroEnded );
    Emitter.once( EXP_RAYCAST_TOGGLE , this.toggleRaycast );

  }

  bind() {

    [ 'update', 'onIntroEnded', 'handleClickOnFish', 'onMouseMove', 'raycast', 'toggleRaycast' ]
        .forEach( ( fn ) => this[ fn ] = this[ fn ].bind( this ) );
  }

  addEventListeners() {

    document.addEventListener( 'click', this.handleClickOnFish, false );
    document.addEventListener( 'mousemove', this.onMouseMove, false );
  }

  removeEventListeners() {

    document.removeEventListener( 'click', this.handleClickOnFish, false );
    document.removeEventListener( 'mousemove', this.onMouseMove, false );
  }

  onIntroEnded() {

    this.raycastEnabled = true;

    this.addEventListeners();
  }

  toggleRaycast( toggle ) {
    this.raycastEnabled = toggle;
  }

  onMouseMove( ev ) {

    this.mouse.x = ( ev.clientX / window.innerWidth ) * 2 - 1;
    this.mouse.y = - ( ev.clientY / window.innerHeight ) * 2 + 1;
  }

  handleClickOnFish() {

    if( this.intersects.length <= 0 ) return;

    const model = this.intersects[ 0 ].object;
    const fish = model.parent.parent;

    const luxGain = randomFloat( 0.05, 0.1 );

    TweenMax.to( model.scale, 1, { x: 0.001, y: 0.001, z: 0.001, ease: Expo.easeOut, onComplete: () => {

      Emitter.emit( EXP_LUX_VALUE_UPDATE, luxGain );

      Emitter.emit( EXP_FLASH_MSG, 'good', `You win + ${luxGain} lux` );

      model.removeFish( fish );

      this.fishCounter++;

      // Win :tada:
      if( this.fishCounter >= this.fishGoal ) {

        Emitter.emit( EXP_SHOW_VIDEO );
      }

    } });
  }

  updateWindowCursorPointer() {

    if( this.isIntersecting ) {

      document.body.classList.add( 'is-intersecting' );
    } else {

      document.body.classList.remove( 'is-intersecting' );
    }

  }

  raycast() {

    if( this.raycastEnabled ) {

      this.raycaster.setFromCamera( this.mouse, this.camera );

      // calculate objects intersecting the picking ray
      this.intersects = this.raycaster.intersectObjects( this.fishModels );

      this.wasIntersecting = this.isIntersecting;

      if( this.intersects.length > 0 ) {

        // Check distance
        if( this.intersects[ 0 ].distance < 2000 ) {

          this.isIntersecting = true;

          if( this.wasIntersecting !== this.isIntersecting ) {

            // Debounce later system
            clearTimeout( this.intersectingTimeout );

            this.intersectingTimeout = setTimeout( () => {
              this.intersectingDebounce = false;
            }, 300 );

            // Debounce trigger only once per 300ms
            if( !this.intersectingDebounce ) {

              this.intersectingDebounce = true;

              Emitter.emit( EXP_INTERSECTING_FISH, this.intersects[ 0 ] );
              Emitter.emit( EXP_SHOW_FISH_NAME, this.intersects[ 0 ].object.name );

              // Play sound
              SoundManager.play( 'fish-hover' );

              this.hoveredFish = this.intersects[ 0 ].object.parentClass;
              this.hoveredFish.hover();
            }

          }

        }

      } else {

        this.isIntersecting = false;

        if( this.wasIntersecting !== this.isIntersecting ) {

          Emitter.emit( EXP_NOT_INTERSECTING_FISH );

          this.hoveredFish.unhover();
        }
      }

      this.updateWindowCursorPointer();

    }
  }

  update( time ) {

    this.raycast();

    this.norm = loopIndex( this.norm + 0.001, 1 );

    this.fishGroups.map( group => {
      group.update( time );
    });
  }
}

export default Level;
<|endoftext|>


<commit_before>const https = require('https');
const AWS = require('aws-sdk');

const sns = new AWS.SNS({
  apiVersion: '2010-03-31',
  region: process.env.SLACK_MESSAGE_RELAY_SNS_TOPIC_ARN.split(':')[3],
});
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });

const CLASSY_API_CLIENT_ID = process.env.CLASSY_API_CLIENT_ID;
const CLASSY_API_CLIENT_SECRET = process.env.CLASSY_API_CLIENT_SECRET;
const POLLING_FREQUENCY = +process.env.POLLING_FREQUENCY;

const mapping = {
  338429: '#radiotopia-donations',
  336384: '#radiotopia-donations',
  330330: '#radiotopia-donations',
  330245: '#radiotopia-donations',
  342678: '#radiotopia-donations',
  327574: '#radiotopia-donations',
  386768: '#radiotopia-donations',
  389319: '#radiotopia-donations',
  326353: '#earhustle-donations',
  368624: '#tw-fall-2021-donations',
  368561: '#tw-fall-2021-donations',
  368367: '#tw-fall-2021-donations',
  // 999999: '#tw-fall-2021-donations',
};

function getAccessToken() {
  return new Promise((resolve, reject) => {
    const body = `grant_type=client_credentials&client_id=${CLASSY_API_CLIENT_ID}&client_secret=${CLASSY_API_CLIENT_SECRET}`;

    const options = {
      host: 'api.classy.org',
      path: `/oauth2/auth`,
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(body),
      },
    };

    const req = https.request(options, (res) => {
      res.setEncoding('utf8');

      let json = '';
      res.on('data', (chunk) => {
        json += chunk;
      });
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          const payload = JSON.parse(json);
          resolve(payload.access_token);
        } else {
          reject(new Error(`Classy token request failed! ${res.statusCode}`));
        }
      });
    });

    // Generic request error handling
    req.on('error', (e) => reject(e));

    req.write(body);
    req.end();
  });
}

function httpGet(token, path) {
  return new Promise((resolve, reject) => {
    const options = {
      host: 'api.classy.org',
      path: `/2.0${path}`,
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`,
      },
    };

    const req = https.get(options, (res) => {
      res.setEncoding('utf8');

      let json = '';
      res.on('data', (chunk) => {
        json += chunk;
      });
      res.on('end', () => {
        try {
          const resPayload = JSON.parse(json);
          resolve(resPayload);
        } catch (e) {
          console.error('Error handling JSON response!');
          reject(e);
        }
      });
    });

    // Generic request error handling
    req.on('error', (e) => reject(e));
  });
}

function getCount() {
  return new Promise((resolve, reject) => {
    s3.getObject(
      {
        Bucket: process.env.COUNTER_BUCKET,
        Key: process.env.COUNTER_OBJECT,
      },
      (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(+data.Body.toString('utf8'));
        }
      },
    );
  });
}

function shortMemberName(member) {
  return `${member?.first_name} ${member?.last_name.charAt(0)}.`;
}

function campaignUrl(campaign) {
  return `https://www.classy.org/manage/event/${campaign.id}/overview`;
}

function transactionUrl(transaction) {
  return `https://www.classy.org/admin/72482/transactions/${transaction.id}`;
}

function moneyAmountString(transaction) {
  const currency =
    transaction?.metadata?.['_classy_pay']?.transaction?.currency ||
    transaction.currency_code;
  const rawAmount =
    transaction?.metadata?.['_classy_pay']?.transaction?.amount ||
    transaction.raw_donation_gross_amount;
  const amount = (+rawAmount).toFixed(2);

  let money;
  if (currency === 'USD') {
    money = `$${amount}`;
  } else if (currency === 'GBP') {
    money = `£${amount}`;
  } else if (currency === 'CAD') {
    money = `CA$${amount}`;
  } else if (currency === 'EUR') {
    money = `€${amount}`;
  } else {
    money = `${amount} ${currency}`;
  }

  if (
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeCurrency &&
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeAmount &&
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeCurrency !==
      currency
  ) {
    const t = transaction?.metadata?.['_classy_pay']?.transaction;
    const ca = +t?.chargeAmount;
    const cc = t?.chargeCurrency;

    if (cc === 'USD') {
      money = `${money} ($${ca.toFixed(2)})`;
    } else {
      money = `${money} (${ca.toFixed(2)} ${cc})`;
    }
  }

  return money;
}

exports.handler = async (event) => {
  const token = await getAccessToken();

  const payload = await httpGet(token, `/organizations/72482/activity`);

  const now = new Date();
  const threshold = +now - POLLING_FREQUENCY * 60 * 1000;
  // const threshold = +now - 6 * 60 * 1000;

  for (const activity of payload.data) {
    if (activity.campaign) {
      let text;
      const camp = activity.campaign;

      if (activity.type === 'donation_created') {
        const tx = activity.transaction;
        const mem = activity.member;

        const ts = Date.parse(activity.created_at);

        // Only process transactions from the campaigns we care about, since
        // the last time the the poller ran
        if (mapping[camp.id] && ts >= threshold) {
          const comment = tx.comment?.length ? `\n> ${tx.comment}` : '';
          text = '';

          // For transactions that are part of the recurring donation plan,
          // if the transaction date is after the plan's start date, we ignore
          // the activity. We only care about the initial transaction for
          // recurring plans.
          if (tx?.metadata?._classy_pay?.transaction?.metaData?.planId) {
            const rdpId = tx.metadata._classy_pay.transaction.metaData.planId;
            const recPlan = await httpGet(
              token,
              `/recurring-donation-plans/${rdpId}`,
            );

            // This is when the transaction actually started, which could be days
            // before the activity showed up (like with ACH transactions).
            const txCreatedDate =
              tx?.metadata?._classy_pay?.transaction?.createdDate;

            // This is midnight of the day the plan started
            const recPlanStartedDate = recPlan?.started_at;

            if (txCreatedDate && recPlanStartedDate) {
              const txDate = txCreatedDate.slice(0, 10);
              const planDate = recPlanStartedDate.slice(0, 10);

              // Compare the YYYY-MM-DD dates
              if (txDate > planDate) {
                console.log('Skipping recurring donation plan activity');
                text = text.concat(':recycle:');
                continue;
              }
            }
          }

          // const fullTx = await httpGet(`/transactions/${tx.id}`);

          const name = shortMemberName(mem);
          const campUrl = campaignUrl(camp);
          const txUrl = transactionUrl(tx);
          // const supporterUrl = `https://www.classy.org/admin/72482/supporters/${fullTx.supporter_id}`;

          const money = moneyAmountString(tx);

          if (tx.frequency === 'one-time') {
            text = text.concat(
              `*${name}* made a <${txUrl}|${money}> donation to the <${campUrl}|${camp.name}> campaign${comment}`,
            );
          } else {
            text = text.concat(
              `*${name}* created a new ${tx.frequency} recurring giving plan for the <${campUrl}|${camp.name}> campaign for <${txUrl}|${money}>${comment}`,
            );
          }

          // if (mapping[camp.id] === '#radiotopia-donations') {
          //   const count = await getCount();
          //   await s3
          //     .putObject({
          //       Body: `${count + 1}`,
          //       Bucket: process.env.COUNTER_BUCKET,
          //       Key: process.env.COUNTER_OBJECT,
          //     })
          //     .promise();

          //   text = `${text} (#${count})`;
          // }
        }
      } else if (activity.type === 'ticket_purchased') {
        const tx = activity.transaction;
        const mem = activity.member;

        const ts = Date.parse(activity.created_at);

        // Only process transactions from the campaigns we care about, since
        // the last time the the poller ran
        if (mapping[camp.id] && ts >= threshold) {
          const name = shortMemberName(mem);
          const campUrl = campaignUrl(camp);
          const txUrl = transactionUrl(tx);

          const money = moneyAmountString(tx);

          const comment = tx.comment?.length ? `\n> ${tx.comment}` : '';
          text = `:admission_tickets: *${name}* selected a <${txUrl}|${money}> reward from the <${campUrl}|${camp.name}> campaign${comment}`;
        }
      }

      if (text) {
        await sns
          .publish({
            TopicArn: process.env.SLACK_MESSAGE_RELAY_SNS_TOPIC_ARN,
            Message: JSON.stringify({
              channel: mapping[camp.id],
              username: 'Classy',
              icon_emoji: ':classy:',
              text,
            }),
          })
          .promise();
      }
    }
  }
};
<commit_msg>Add Ear Hustle mappings<commit_after>const https = require('https');
const AWS = require('aws-sdk');

const sns = new AWS.SNS({
  apiVersion: '2010-03-31',
  region: process.env.SLACK_MESSAGE_RELAY_SNS_TOPIC_ARN.split(':')[3],
});
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });

const CLASSY_API_CLIENT_ID = process.env.CLASSY_API_CLIENT_ID;
const CLASSY_API_CLIENT_SECRET = process.env.CLASSY_API_CLIENT_SECRET;
const POLLING_FREQUENCY = +process.env.POLLING_FREQUENCY;

const mapping = {
  338429: '#radiotopia-donations',
  336384: '#radiotopia-donations',
  330330: '#radiotopia-donations',
  330245: '#radiotopia-donations',
  342678: '#radiotopia-donations',
  327574: '#radiotopia-donations',
  386768: '#radiotopia-donations',
  389319: '#radiotopia-donations',
  326353: '#earhustle-donations',
  399428: '#earhustle-donations',
  400513: '#earhustle-donations',
  368624: '#tw-fall-2021-donations',
  368561: '#tw-fall-2021-donations',
  368367: '#tw-fall-2021-donations',
  // 999999: '#tw-fall-2021-donations',
};

function getAccessToken() {
  return new Promise((resolve, reject) => {
    const body = `grant_type=client_credentials&client_id=${CLASSY_API_CLIENT_ID}&client_secret=${CLASSY_API_CLIENT_SECRET}`;

    const options = {
      host: 'api.classy.org',
      path: `/oauth2/auth`,
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(body),
      },
    };

    const req = https.request(options, (res) => {
      res.setEncoding('utf8');

      let json = '';
      res.on('data', (chunk) => {
        json += chunk;
      });
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          const payload = JSON.parse(json);
          resolve(payload.access_token);
        } else {
          reject(new Error(`Classy token request failed! ${res.statusCode}`));
        }
      });
    });

    // Generic request error handling
    req.on('error', (e) => reject(e));

    req.write(body);
    req.end();
  });
}

function httpGet(token, path) {
  return new Promise((resolve, reject) => {
    const options = {
      host: 'api.classy.org',
      path: `/2.0${path}`,
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`,
      },
    };

    const req = https.get(options, (res) => {
      res.setEncoding('utf8');

      let json = '';
      res.on('data', (chunk) => {
        json += chunk;
      });
      res.on('end', () => {
        try {
          const resPayload = JSON.parse(json);
          resolve(resPayload);
        } catch (e) {
          console.error('Error handling JSON response!');
          reject(e);
        }
      });
    });

    // Generic request error handling
    req.on('error', (e) => reject(e));
  });
}

function getCount() {
  return new Promise((resolve, reject) => {
    s3.getObject(
      {
        Bucket: process.env.COUNTER_BUCKET,
        Key: process.env.COUNTER_OBJECT,
      },
      (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(+data.Body.toString('utf8'));
        }
      },
    );
  });
}

function shortMemberName(member) {
  return `${member?.first_name} ${member?.last_name.charAt(0)}.`;
}

function campaignUrl(campaign) {
  return `https://www.classy.org/manage/event/${campaign.id}/overview`;
}

function transactionUrl(transaction) {
  return `https://www.classy.org/admin/72482/transactions/${transaction.id}`;
}

function moneyAmountString(transaction) {
  const currency =
    transaction?.metadata?.['_classy_pay']?.transaction?.currency ||
    transaction.currency_code;
  const rawAmount =
    transaction?.metadata?.['_classy_pay']?.transaction?.amount ||
    transaction.raw_donation_gross_amount;
  const amount = (+rawAmount).toFixed(2);

  let money;
  if (currency === 'USD') {
    money = `$${amount}`;
  } else if (currency === 'GBP') {
    money = `£${amount}`;
  } else if (currency === 'CAD') {
    money = `CA$${amount}`;
  } else if (currency === 'EUR') {
    money = `€${amount}`;
  } else {
    money = `${amount} ${currency}`;
  }

  if (
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeCurrency &&
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeAmount &&
    transaction?.metadata?.['_classy_pay']?.transaction?.chargeCurrency !==
      currency
  ) {
    const t = transaction?.metadata?.['_classy_pay']?.transaction;
    const ca = +t?.chargeAmount;
    const cc = t?.chargeCurrency;

    if (cc === 'USD') {
      money = `${money} ($${ca.toFixed(2)})`;
    } else {
      money = `${money} (${ca.toFixed(2)} ${cc})`;
    }
  }

  return money;
}

exports.handler = async (event) => {
  const token = await getAccessToken();

  const payload = await httpGet(token, `/organizations/72482/activity`);

  const now = new Date();
  const threshold = +now - POLLING_FREQUENCY * 60 * 1000;
  // const threshold = +now - 6 * 60 * 1000;

  for (const activity of payload.data) {
    if (activity.campaign) {
      let text;
      const camp = activity.campaign;

      if (activity.type === 'donation_created') {
        const tx = activity.transaction;
        const mem = activity.member;

        const ts = Date.parse(activity.created_at);

        // Only process transactions from the campaigns we care about, since
        // the last time the the poller ran
        if (mapping[camp.id] && ts >= threshold) {
          const comment = tx.comment?.length ? `\n> ${tx.comment}` : '';
          text = '';

          // For transactions that are part of the recurring donation plan,
          // if the transaction date is after the plan's start date, we ignore
          // the activity. We only care about the initial transaction for
          // recurring plans.
          if (tx?.metadata?._classy_pay?.transaction?.metaData?.planId) {
            const rdpId = tx.metadata._classy_pay.transaction.metaData.planId;
            const recPlan = await httpGet(
              token,
              `/recurring-donation-plans/${rdpId}`,
            );

            // This is when the transaction actually started, which could be days
            // before the activity showed up (like with ACH transactions).
            const txCreatedDate =
              tx?.metadata?._classy_pay?.transaction?.createdDate;

            // This is midnight of the day the plan started
            const recPlanStartedDate = recPlan?.started_at;

            if (txCreatedDate && recPlanStartedDate) {
              const txDate = txCreatedDate.slice(0, 10);
              const planDate = recPlanStartedDate.slice(0, 10);

              // Compare the YYYY-MM-DD dates
              if (txDate > planDate) {
                console.log('Skipping recurring donation plan activity');
                text = text.concat(':recycle:');
                continue;
              }
            }
          }

          // const fullTx = await httpGet(`/transactions/${tx.id}`);

          const name = shortMemberName(mem);
          const campUrl = campaignUrl(camp);
          const txUrl = transactionUrl(tx);
          // const supporterUrl = `https://www.classy.org/admin/72482/supporters/${fullTx.supporter_id}`;

          const money = moneyAmountString(tx);

          if (tx.frequency === 'one-time') {
            text = text.concat(
              `*${name}* made a <${txUrl}|${money}> donation to the <${campUrl}|${camp.name}> campaign${comment}`,
            );
          } else {
            text = text.concat(
              `*${name}* created a new ${tx.frequency} recurring giving plan for the <${campUrl}|${camp.name}> campaign for <${txUrl}|${money}>${comment}`,
            );
          }

          // if (mapping[camp.id] === '#radiotopia-donations') {
          //   const count = await getCount();
          //   await s3
          //     .putObject({
          //       Body: `${count + 1}`,
          //       Bucket: process.env.COUNTER_BUCKET,
          //       Key: process.env.COUNTER_OBJECT,
          //     })
          //     .promise();

          //   text = `${text} (#${count})`;
          // }
        }
      } else if (activity.type === 'ticket_purchased') {
        const tx = activity.transaction;
        const mem = activity.member;

        const ts = Date.parse(activity.created_at);

        // Only process transactions from the campaigns we care about, since
        // the last time the the poller ran
        if (mapping[camp.id] && ts >= threshold) {
          const name = shortMemberName(mem);
          const campUrl = campaignUrl(camp);
          const txUrl = transactionUrl(tx);

          const money = moneyAmountString(tx);

          const comment = tx.comment?.length ? `\n> ${tx.comment}` : '';
          text = `:admission_tickets: *${name}* selected a <${txUrl}|${money}> reward from the <${campUrl}|${camp.name}> campaign${comment}`;
        }
      }

      if (text) {
        await sns
          .publish({
            TopicArn: process.env.SLACK_MESSAGE_RELAY_SNS_TOPIC_ARN,
            Message: JSON.stringify({
              channel: mapping[camp.id],
              username: 'Classy',
              icon_emoji: ':classy:',
              text,
            }),
          })
          .promise();
      }
    }
  }
};
<|endoftext|>


<commit_before>/* @flow */
import {Dryad} from 'dryadic';
import {synthNew, nodeFree, AddActions} from '../server/osc/msg';
import {whenNodeGo, whenNodeEnd, updateNodeState} from '../server/node-watcher';
import * as _  from 'lodash';

/**
 * Creates a synth on the server.
 *
 * Properties:
 * - def
 * - args
 */
export default class Synth extends Dryad {

  /**
   * If there is no SCServer in the parent context,
   * then this will wrap itself in an SCServer
   */
  requireParent() : string {
    return 'SCServer';
  }

  prepareForAdd() : Object {
    return {
      updateContext: (context) => ({
        nodeID: context.scserver.state.nextNodeID()
      })
    };
  }

  // synthDefName(context:Object) : string {
  //   // The parent SCSynthDef publishes both .synthDef (object) and .synthDefName to context
  //   let name = _.isString(this.properties.def) ? this.properties.def : (context.synthDef && context.synthDef.name);
  //   if (!name) {
  //     throw new Error('No synthDefName supplied to Synth', context);
  //   }
  //   return name;
  // }

  add() : Object {
    return {
      scserver: {
        msg: (context, properties) => {
          let args = _.mapValues(properties.args, (value, key) => this._checkOscType(value, key, context.id));
          let defName = this._checkOscType(properties.def, 'synthDefName', context.id);
          return synthNew(defName, context.nodeID, AddActions.TAIL, context.group, args);
        }
      },
      run: (context, properties) => {
        return whenNodeGo(context.scserver, context.id, context.nodeID)
          .then((nodeID) => {
            // TODO: call a method instead so its testable
            updateNodeState(context.scserver, context.nodeID, {synthDef: properties.def});
            return nodeID;
          });
      }
    };
  }

  remove() : Object {
    return {
      scserver: {
        msg: (context) => nodeFree(context.nodeID)
      },
      run: (context) => whenNodeEnd(context.scserver, context.id, context.nodeID)
    };
  }

  _checkOscType(v:any, key:string, id:string) : any {
    switch (typeof v) {
      case 'number':
      case 'string':
      // case 'Buffer':
        return v;
      default:
        throw new Error(`Invalid OSC type for Synth ${key}: [${typeof v}: ${v}] @ ${id}`);
    }
  }
}
<commit_msg>Synth: set 'out' to context.out if not already set<commit_after>/* @flow */
import {Dryad} from 'dryadic';
import {synthNew, nodeFree, AddActions} from '../server/osc/msg';
import {whenNodeGo, whenNodeEnd, updateNodeState} from '../server/node-watcher';
import * as _  from 'lodash';

/**
 * Creates a synth on the server.
 *
 * Properties:
 * - def
 * - args
 */
export default class Synth extends Dryad {

  /**
   * If there is no SCServer in the parent context,
   * then this will wrap itself in an SCServer
   */
  requireParent() : string {
    return 'SCServer';
  }

  prepareForAdd() : Object {
    return {
      updateContext: (context) => ({
        nodeID: context.scserver.state.nextNodeID()
      })
    };
  }

  // synthDefName(context:Object) : string {
  //   // The parent SCSynthDef publishes both .synthDef (object) and .synthDefName to context
  //   let name = _.isString(this.properties.def) ? this.properties.def : (context.synthDef && context.synthDef.name);
  //   if (!name) {
  //     throw new Error('No synthDefName supplied to Synth', context);
  //   }
  //   return name;
  // }

  add() : Object {
    return {
      scserver: {
        msg: (context, properties) => {
          let args = _.mapValues(properties.args, (value, key) => this._checkOscType(value, key, context.id));
          // if out is not set in args and out is in synthdef
          // then set it from context
          // TODO: check that synthDef has an arg named out
          if (_.isUndefined(args.out) && !_.isUndefined(context.out)) {
            args.out = context.out;
          }

          let defName = this._checkOscType(properties.def, 'synthDefName', context.id);
          return synthNew(defName, context.nodeID, AddActions.TAIL, context.group, args);
        }
      },
      run: (context, properties) => {
        return whenNodeGo(context.scserver, context.id, context.nodeID)
          .then((nodeID) => {
            // TODO: call a method instead so its testable
            updateNodeState(context.scserver, context.nodeID, {synthDef: properties.def});
            return nodeID;
          });
      }
    };
  }

  remove() : Object {
    return {
      scserver: {
        msg: (context) => nodeFree(context.nodeID)
      },
      run: (context) => whenNodeEnd(context.scserver, context.id, context.nodeID)
    };
  }

  _checkOscType(v:any, key:string, id:string) : any {
    switch (typeof v) {
      case 'number':
      case 'string':
      // case 'Buffer':
        return v;
      default:
        throw new Error(`Invalid OSC type for Synth ${key}: [${typeof v}: ${v}] @ ${id}`);
    }
  }
}
<|endoftext|>


<commit_before>const SQL = require('sql-template-strings');
const { dbMiddleware } = require('~/database');
const trackRegister = require('~/trackRegister');

module.exports = router => {
  for (const method of ['post', 'get']) {
    router[method]('/track/:token', dbMiddleware(), handler);
  }
};

async function handler(ctx) {
  const [item] = await ctx.state.db.query(
    SQL`SELECT id, maxCount, maxAge FROM trackingDevice WHERE token = ${ctx.params.token}`,
  );

  if (!item) {
    ctx.throw(404);
  }

  const q =
    ctx.method === 'POST' &&
    ctx.request.type === 'application/x-www-form-urlencoded'
      ? ctx.request.body
      : ctx.query;

  const { message } = q;

  const lat = Number.parseFloat(q.lat);

  const lon = Number.parseFloat(q.lon);

  const altitude =
    (q.alt || q.altitude) === undefined
      ? null
      : Number.parseFloat(q.alt || q.altitude);
  const speedMs = q.speed === undefined ? null : Number.parseFloat(q.speed);

  const speedKmh =
    q.speedKmh === undefined ? null : Number.parseFloat(q.speedKmh);

  const accuracy = q.acc === undefined ? null : Number.parseFloat(q.acc);

  const hdop = q.hdop === undefined ? null : Number.parseFloat(q.hdop);

  const bearing = q.bearing === undefined ? null : Number.parseFloat(q.bearing);

  const battery = q.battery === undefined ? null : Number.parseFloat(q.battery);

  const gsmSignal =
    q.gsm_signal === undefined ? null : Number.parseFloat(q.gsm_signal);

  const time = guessTime(q.time || q.timestamp);

  if (
    time === null ||
    Number.isNaN(lat) ||
    lat < -90 ||
    lat > 90 ||
    Number.isNaN(lon) ||
    lon < -180 ||
    lon > 180 ||
    Number.isNaN(battery) ||
    (battery !== null && (battery < 0 || battery > 100)) ||
    Number.isNaN(gsmSignal) ||
    (gsmSignal !== null && (gsmSignal < 0 || gsmSignal > 100)) ||
    Number.isNaN(bearing) ||
    (bearing !== null && (bearing < 0 || bearing > 360)) ||
    Number.isNaN(accuracy) ||
    (accuracy !== null && accuracy < 0) ||
    Number.isNaN(hdop) ||
    (hdop !== null && hdop < 0) ||
    Number.isNaN(speedMs) ||
    (speedMs !== null && speedMs < 0) ||
    Number.isNaN(speedKmh) ||
    (speedKmh !== null && speedKmh < 0)
  ) {
    ctx.throw(400);
  }

  const now = new Date();

  const { id, maxAge, maxCount } = item;

  const speed = typeof speedKmh === 'number' ? speedKmh / 3.6 : speedMs;

  const { insertId } = await ctx.state.db.query(SQL`
    INSERT INTO trackingPoint SET
      deviceId = ${id},
      lat = ${lat},
      lon = ${lon},
      altitude = ${altitude},
      speed = ${speed},
      accuracy = ${accuracy},
      hdop = ${hdop},
      bearing = ${bearing},
      battery = ${battery},
      gsmSignal = ${gsmSignal},
      message = ${message},
      createdAt = ${time}
  `);

  if (maxAge) {
    await ctx.state.db.query(
      SQL`DELETE FROM trackingPoint WHERE deviceId = ${id} AND TIMESTAMPDIFF(SECOND, createdAt, now()) > ${maxAge}`,
    );
  }

  if (maxCount) {
    await ctx.state.db.query(SQL`
      DELETE t FROM trackingPoint AS t JOIN (
        SELECT id FROM trackingPoint WHERE deviceId = ${id}
          ORDER BY id DESC LIMIT 18446744073709551615 OFFSET ${maxCount + 1}
      ) tlimit ON t.id = tlimit.id`);
  }

  const rows = await ctx.state.db.query(SQL`
    SELECT token FROM trackingAccessToken
      WHERE deviceId = ${id} AND (timeFrom IS NULL OR timeFrom > ${now}) AND (timeTo IS NULL OR timeTo < ${now})
  `);

  const notify = (type, key) => {
    const websockets = trackRegister.get(key);

    if (websockets) {
      for (const ws of websockets) {
        if (ws.readyState === 1) {
          ws.send(
            JSON.stringify({
              jsonrpc: '2.0',
              method: 'tracking.addPoint',
              params: {
                // TODO validate if time matches limits
                id: insertId,
                lat,
                lon,
                altitude,
                speed,
                accuracy,
                hdop,
                bearing,
                battery,
                gsmSignal,
                message,
                [type]: key,
                ts: time.toISOString(),
              },
            }),
          );
        }
      }
    }
  };

  for (const { token } of rows) {
    notify('token', token);
  }

  notify('deviceId', id);

  ctx.body = { id: insertId };
}

function guessTime(t) {
  const now = new Date();

  const min = new Date();

  min.setDate(min.getDate() - 2);

  const max = new Date();

  max.setDate(max.getDate() + 1);

  if (!t) {
    return now;
  }

  const d1 = new Date(t);

  if (max > d1 && d1 > min) {
    return d1;
  }

  const n = Number.parseInt(t, 10);

  if (Number.isNaN(n)) {
    return null;
  }

  const d2 = new Date(n);

  if (max > d2 && d2 > min) {
    return d2;
  }

  const d3 = new Date(n * 1000);

  if (max > d3 && d3 > min) {
    return d3;
  }

  return null;
}
<commit_msg>spacing<commit_after>const SQL = require('sql-template-strings');
const { dbMiddleware } = require('~/database');
const trackRegister = require('~/trackRegister');

module.exports = router => {
  for (const method of ['post', 'get']) {
    router[method]('/track/:token', dbMiddleware(), handler);
  }
};

async function handler(ctx) {
  const [item] = await ctx.state.db.query(
    SQL`SELECT id, maxCount, maxAge FROM trackingDevice WHERE token = ${ctx.params.token}`,
  );

  if (!item) {
    ctx.throw(404);
  }

  const q =
    ctx.method === 'POST' &&
    ctx.request.type === 'application/x-www-form-urlencoded'
      ? ctx.request.body
      : ctx.query;

  const { message } = q;

  const lat = Number.parseFloat(q.lat);

  const lon = Number.parseFloat(q.lon);

  const altitude =
    (q.alt || q.altitude) === undefined
      ? null
      : Number.parseFloat(q.alt || q.altitude);

  const speedMs = q.speed === undefined ? null : Number.parseFloat(q.speed);

  const speedKmh =
    q.speedKmh === undefined ? null : Number.parseFloat(q.speedKmh);

  const accuracy = q.acc === undefined ? null : Number.parseFloat(q.acc);

  const hdop = q.hdop === undefined ? null : Number.parseFloat(q.hdop);

  const bearing = q.bearing === undefined ? null : Number.parseFloat(q.bearing);

  const battery = q.battery === undefined ? null : Number.parseFloat(q.battery);

  const gsmSignal =
    q.gsm_signal === undefined ? null : Number.parseFloat(q.gsm_signal);

  const time = guessTime(q.time || q.timestamp);

  if (
    time === null ||
    Number.isNaN(lat) ||
    lat < -90 ||
    lat > 90 ||
    Number.isNaN(lon) ||
    lon < -180 ||
    lon > 180 ||
    Number.isNaN(battery) ||
    (battery !== null && (battery < 0 || battery > 100)) ||
    Number.isNaN(gsmSignal) ||
    (gsmSignal !== null && (gsmSignal < 0 || gsmSignal > 100)) ||
    Number.isNaN(bearing) ||
    (bearing !== null && (bearing < 0 || bearing > 360)) ||
    Number.isNaN(accuracy) ||
    (accuracy !== null && accuracy < 0) ||
    Number.isNaN(hdop) ||
    (hdop !== null && hdop < 0) ||
    Number.isNaN(speedMs) ||
    (speedMs !== null && speedMs < 0) ||
    Number.isNaN(speedKmh) ||
    (speedKmh !== null && speedKmh < 0)
  ) {
    ctx.throw(400);
  }

  const now = new Date();

  const { id, maxAge, maxCount } = item;

  const speed = typeof speedKmh === 'number' ? speedKmh / 3.6 : speedMs;

  const { insertId } = await ctx.state.db.query(SQL`
    INSERT INTO trackingPoint SET
      deviceId = ${id},
      lat = ${lat},
      lon = ${lon},
      altitude = ${altitude},
      speed = ${speed},
      accuracy = ${accuracy},
      hdop = ${hdop},
      bearing = ${bearing},
      battery = ${battery},
      gsmSignal = ${gsmSignal},
      message = ${message},
      createdAt = ${time}
  `);

  if (maxAge) {
    await ctx.state.db.query(
      SQL`DELETE FROM trackingPoint WHERE deviceId = ${id} AND TIMESTAMPDIFF(SECOND, createdAt, now()) > ${maxAge}`,
    );
  }

  if (maxCount) {
    await ctx.state.db.query(SQL`
      DELETE t FROM trackingPoint AS t JOIN (
        SELECT id FROM trackingPoint WHERE deviceId = ${id}
          ORDER BY id DESC LIMIT 18446744073709551615 OFFSET ${maxCount + 1}
      ) tlimit ON t.id = tlimit.id`);
  }

  const rows = await ctx.state.db.query(SQL`
    SELECT token FROM trackingAccessToken
      WHERE deviceId = ${id} AND (timeFrom IS NULL OR timeFrom > ${now}) AND (timeTo IS NULL OR timeTo < ${now})
  `);

  const notify = (type, key) => {
    const websockets = trackRegister.get(key);

    if (websockets) {
      for (const ws of websockets) {
        if (ws.readyState === 1) {
          ws.send(
            JSON.stringify({
              jsonrpc: '2.0',
              method: 'tracking.addPoint',
              params: {
                // TODO validate if time matches limits
                id: insertId,
                lat,
                lon,
                altitude,
                speed,
                accuracy,
                hdop,
                bearing,
                battery,
                gsmSignal,
                message,
                [type]: key,
                ts: time.toISOString(),
              },
            }),
          );
        }
      }
    }
  };

  for (const { token } of rows) {
    notify('token', token);
  }

  notify('deviceId', id);

  ctx.body = { id: insertId };
}

function guessTime(t) {
  const now = new Date();

  const min = new Date();

  min.setDate(min.getDate() - 2);

  const max = new Date();

  max.setDate(max.getDate() + 1);

  if (!t) {
    return now;
  }

  const d1 = new Date(t);

  if (max > d1 && d1 > min) {
    return d1;
  }

  const n = Number.parseInt(t, 10);

  if (Number.isNaN(n)) {
    return null;
  }

  const d2 = new Date(n);

  if (max > d2 && d2 > min) {
    return d2;
  }

  const d3 = new Date(n * 1000);

  if (max > d3 && d3 > min) {
    return d3;
  }

  return null;
}
<|endoftext|>


<commit_before>'use strict';

var cachedTabs = [];

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
  if (changeInfo.status !== 'loading') return;

  chrome.tabs.executeScript(tabId, {
    code: 'var injected = window.octotreeInjected; window.octotreeInjected = true; console.log("INJECTING DATA '+ tabId + '"); injected;',
    runAt: 'document_start'
  }, function (res) {
    if (chrome.runtime.lastError || // don't continue if error (i.e. page isn't in permission list)
    res[0]) // value of `injected` above: don't inject twice
      return;

    var cssFiles = ['jstree.css', 'octotree.css'];

    var jsFiles = ['jquery.js', 'jquery-ui.js', 'jstree.js'];

    cachedTabs[tabId] = true;

    eachTask([function (cb) {
      return eachItem(cssFiles, inject('insertCSS'), cb);
    }, function (cb) {
      return eachItem(jsFiles, inject('executeScript'), cb);
    }]);

    function inject(fn) {
      return function (file, cb) {
        chrome.tabs[fn](tabId, { file: file, runAt: 'document_start' }, cb);
      };
    }
  });
});

var workingEnvironment = [];
var snippetsList = [];

chrome.runtime.onMessage.addListener(function (req, sender, sendRes) {
  var handler = {
    requestPermissions: function requestPermissions() {
      var urls = (req.urls || []).filter(function (url) {
        return url.trim() !== '';
      }).map(function (url) {
        if (url.slice(-2) === '/*') return url;
        if (url.slice(-1) === '/') return url + '*';
        return url + '/*';
      });

      if (urls.length === 0) {
        sendRes(true);
        removeUnnecessaryPermissions();
      } else {
        chrome.permissions.request({ origins: urls }, function (granted) {
          sendRes(granted);
          removeUnnecessaryPermissions();
        });
      }
      return true;

      function removeUnnecessaryPermissions() {
        var whitelist = urls.concat(['https://github.com/*', 'https://gitlab.com/*']);
        chrome.permissions.getAll(function (permissions) {
          var toBeRemovedUrls = permissions.origins.filter(function (url) {
            return ! ~whitelist.indexOf(url);
          });

          if (toBeRemovedUrls.length) {
            chrome.permissions.remove({ origins: toBeRemovedUrls });
          }
        });
      }
    },
    saveSnippet: function gotReportFromTheTab(data) {
      for (var r in cachedTabs) {
        console.log("DO SOME STUFF");
        console.dir(r);
        chrome.tabs.executeScript(parseInt(r), {
          code: 'console.log("BROADCAST !!!!");'
        });
      }
      return true;
    },
    initialItems: function(payload) {
      console.log("GET INITIAL ITEMS: " + sender.tab.id);
      // has opened snippet
      console.dir(workingEnvironment);
      var pos = workingEnvironment[sender.tab.id];
      if (pos != undefined && pos >= 0) {
        return sendRes({working: pos, snippets: snippetsList});
      }
      console.log("GET INITIAL ITEMS EMPTY");
      sendRes({snippets: snippetsList});
    },
    createSnippet: function(payload) {
      console.log("CREATE SNIPPET !Q!!!! " + snippetsList.length );
      // init snippet by id
      workingEnvironment[sender.tab.id] = snippetsList.length;
      snippetsList.push({title: payload.title, items:[]});
      sendRes({working: workingEnvironment[sender.tab.id]});
    },
    closeSnippet: function(data) {
      workingEnvironment[sender.tab.id] = null;
    },
    addNewItem: function(payload) {
       var pos = workingEnvironment[sender.tab.id];
       console.log("POSITION IS: " + pos);
       console.dir(payload);
       if (pos != undefined) {
         console.log("ADD PAYLOAD");
         snippetsList[pos].items.push(payload);
         sendRes(true);
         return true;
       }
      sendRes(false);
       return false;

    },
    openItem: function(itemId) {
      console.dir(sender.tab);
      chrome.tabs.create({'url': "http://google.com", active:true}, function(x) {
        console.dir(arguments);
      });
      sendRes(true);
    }
  };
  console.log("HANDLE :" + req.type);
  return handler[req.type](req.payload);
});

function eachTask(tasks, done) {
  (function next() {
    var index = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];

    if (index === tasks.length) done && done();else tasks[index](function () {
      return next(++index);
    });
  })();
}

function eachItem(arr, iter, done) {
  var tasks = arr.map(function (item) {
    return function (cb) {
      return iter(item, cb);
    };
  });
  return eachTask(tasks, done);
}
<commit_msg>Same origin redirect url.<commit_after>'use strict';

var cachedTabs = [];

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
  if (changeInfo.status !== 'loading') return;

  chrome.tabs.executeScript(tabId, {
    code: 'var injected = window.octotreeInjected; window.octotreeInjected = true; console.log("INJECTING DATA '+ tabId + '"); injected;',
    runAt: 'document_start'
  }, function (res) {
    if (chrome.runtime.lastError || // don't continue if error (i.e. page isn't in permission list)
    res[0]) // value of `injected` above: don't inject twice
      return;

    var cssFiles = ['jstree.css', 'octotree.css'];

    var jsFiles = ['jquery.js', 'jquery-ui.js', 'jstree.js'];

    cachedTabs[tabId] = true;

    eachTask([function (cb) {
      return eachItem(cssFiles, inject('insertCSS'), cb);
    }, function (cb) {
      return eachItem(jsFiles, inject('executeScript'), cb);
    }]);

    function inject(fn) {
      return function (file, cb) {
        chrome.tabs[fn](tabId, { file: file, runAt: 'document_start' }, cb);
      };
    }
  });
});

var workingEnvironment = [];
var snippetsList = [];

chrome.runtime.onMessage.addListener(function (req, sender, sendRes) {
  var handler = {
    requestPermissions: function requestPermissions() {
      var urls = (req.urls || []).filter(function (url) {
        return url.trim() !== '';
      }).map(function (url) {
        if (url.slice(-2) === '/*') return url;
        if (url.slice(-1) === '/') return url + '*';
        return url + '/*';
      });

      if (urls.length === 0) {
        sendRes(true);
        removeUnnecessaryPermissions();
      } else {
        chrome.permissions.request({ origins: urls }, function (granted) {
          sendRes(granted);
          removeUnnecessaryPermissions();
        });
      }
      return true;

      function removeUnnecessaryPermissions() {
        var whitelist = urls.concat(['https://github.com/*', 'https://gitlab.com/*']);
        chrome.permissions.getAll(function (permissions) {
          var toBeRemovedUrls = permissions.origins.filter(function (url) {
            return ! ~whitelist.indexOf(url);
          });

          if (toBeRemovedUrls.length) {
            chrome.permissions.remove({ origins: toBeRemovedUrls });
          }
        });
      }
    },
    saveSnippet: function gotReportFromTheTab(data) {
      for (var r in cachedTabs) {
        console.log("DO SOME STUFF");
        console.dir(r);
        chrome.tabs.executeScript(parseInt(r), {
          code: 'console.log("BROADCAST !!!!");'
        });
      }
      return true;
    },
    initialItems: function(payload) {
      console.log("GET INITIAL ITEMS: " + sender.tab.id);
      // has opened snippet
      console.dir(workingEnvironment);
      var pos = workingEnvironment[sender.tab.id];
      if (pos != undefined && pos >= 0) {
        return sendRes({working: pos, snippets: snippetsList});
      }
      console.log("GET INITIAL ITEMS EMPTY");
      sendRes({snippets: snippetsList});
    },
    createSnippet: function(payload) {
      console.log("CREATE SNIPPET !Q!!!! " + snippetsList.length );
      // init snippet by id
      workingEnvironment[sender.tab.id] = snippetsList.length;
      snippetsList.push({title: payload.title, items:[]});
      sendRes({working: workingEnvironment[sender.tab.id]});
    },
    closeSnippet: function(data) {
      workingEnvironment[sender.tab.id] = null;
    },
    addNewItem: function(payload) {
       var pos = workingEnvironment[sender.tab.id];
       console.log("POSITION IS: " + pos);
       console.dir(payload);
       if (pos != undefined) {
         console.log("ADD PAYLOAD");
         snippetsList[pos].items.push(payload);
         sendRes(true);
         return true;
       }
      sendRes(false);
       return false;

    },
    openItem: function(itemId) {
      console.log(itemId);
      var pos = workingEnvironment[sender.tab.id];
      if (snippetsList[pos].items.length > itemId) {
        console.dir(snippetsList[pos]);
        var x_url = snippetsList[pos].items[itemId].url;
        var xx_url = x_url.substr(0, x_url.lastIndexOf(":")) + "#L" + x_url.substr(x_url.lastIndexOf(":") + 1);
        console.log(xx_url);
        chrome.tabs.update({'url': xx_url, active:true}, function(x) {
          console.dir(arguments);
        });
      }

      sendRes(true);
    }
  };
  console.log("HANDLE :" + req.type);
  return handler[req.type](req.payload);
});

function eachTask(tasks, done) {
  (function next() {
    var index = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];

    if (index === tasks.length) done && done();else tasks[index](function () {
      return next(++index);
    });
  })();
}

function eachItem(arr, iter, done) {
  var tasks = arr.map(function (item) {
    return function (cb) {
      return iter(item, cb);
    };
  });
  return eachTask(tasks, done);
}
<|endoftext|>