<commit_before>const querystring = require('querystring')
const Constants = require('../../frontend/constants/Constants')

function initCommonQuery () {
  return {
    size: 0,
    aggs: {
      facets: {
        global: {},
        aggs: {}
      },
      byWork: {
        terms: {
          field: 'workUri',
          order: { top: 'desc' },
          size: Constants.maxSearchResults
        },
        aggs: {
          publications: {
            top_hits: {
              size: 1
            }
          },
          top: {
            max: { script: { lang: 'expression', inline: '_score' }}
	  }
        }
      },
      workCount: {
        cardinality: {
          field: 'workUri'
        }
      }
    }
  }
}

function initSimpleQuery (query) {
  return {
    filtered: {
      filter: {
        bool: {
          must: []
        }
      },
      query: {
        bool: {
          should: [
            {
              simple_query_string: {
                query: query,
                default_operator: 'and',
                fields: [ 'mainTitle^2', 'partTitle', 'subject', 'agents^2', 'genre', 'series', 'format', 'mt' ]
              }
            },
            {
              multi_match: {
                query: query,
                fuzziness: "AUTO",
                type: 'cross_fields',
		fields: [ 'mainTitle^2', 'partTitle', 'subject', 'agents^2', 'genre', 'series', 'format', 'mt' ]
              }
            }
          ],
          must: []
        }
      }
    }
  }
}

function initAdvancedQuery (query) {
  return {
    filtered: {
      filter: {
        bool: {
          must: []
        }
      },
      query: {
        query_string: {
          query: query,
          default_operator: 'and'
        }
      }
    }
  }
}

function isAdvancedQuery (queryString) {
  return /[:+-^()"*]/.test(queryString)
}

function simpleSearchBuilder (queryString) {
  const query = initCommonQuery()
  query.query = initSimpleQuery(queryString)
  return query
}

function advancedSearchBuilder (queryString) {
  const query = initCommonQuery()
  query.query = initAdvancedQuery(queryString)
  return query
}

function parseFilters (filtersFromLocationQuery) {
  const filterableFields = Constants.filterableFields
  const filterBuckets = {}
  if (!Array.isArray(filtersFromLocationQuery)) {
    filtersFromLocationQuery = [ filtersFromLocationQuery ]
  }
  filtersFromLocationQuery.forEach(filter => { // ex filter: 'audience_juvenile'
    const split = filter.split('_')
    const filterableField = filterableFields[ split[ 0 ] ]
    const aggregation = filterableField.name
    if (!filterBuckets[ aggregation ]) {
      filterBuckets[ aggregation ] = []
    }
    const val = filterableField.prefix + filter.substring(`${split[ 0 ]}_`.length)
    filterBuckets[ aggregation ].push(val)
  })

  const filters = []
  Object.keys(filterBuckets).forEach(aggregation => {
    filters.push({ aggregation: aggregation, bucket: filterBuckets[ aggregation ] })
  })

  return filters
}

function createMust (field, terms) {
  return {
    terms: {
      [field]: terms
    }
  }
}

module.exports.buildQuery = function (urlQueryString) {
  const params = querystring.parse(urlQueryString)
  const queryString = params.query
  let elasticSearchQuery = {}
  if (isAdvancedQuery(queryString)) {
    elasticSearchQuery = advancedSearchBuilder(queryString)
  } else {
    elasticSearchQuery = simpleSearchBuilder(queryString)
  }
  const filters = parseFilters(params.filter || [])
  const musts = {}
  filters.forEach(filter => {
    const aggregation = filter.aggregation
    const must = createMust(aggregation, filter.bucket)
    musts[ aggregation ] = must
  })

  Object.keys(musts).forEach(aggregation => {
    elasticSearchQuery.query.filtered.filter.bool.must.push(musts[ aggregation ])
  })

  Object.keys(Constants.filterableFields).forEach(key => {
    const field = Constants.filterableFields[ key ]
    const fieldName = field.name
    elasticSearchQuery.aggs.facets.aggs[ fieldName ] = {
      filter: {
        bool: Object.assign({}, elasticSearchQuery.query.filtered.query.bool || { filter: [ elasticSearchQuery.query.filtered.query ] })
      },
      aggs: {
        [fieldName]: {
          terms: {
            field: fieldName,
            size: 0
          }
        }
      }
    }

    const aggregationMusts = []
    Object.keys(musts).forEach(aggregation => {
      const must = musts[ aggregation ]
      if (aggregation !== fieldName) {
        aggregationMusts.push(must)
      }
    })
    elasticSearchQuery.aggs.facets.aggs[ fieldName ].filter.bool.must = aggregationMusts
  })

  return elasticSearchQuery
}
<commit_msg>clean up for lint<commit_after>const querystring = require('querystring')
const Constants = require('../../frontend/constants/Constants')

function initCommonQuery () {
  return {
    size: 0,
    aggs: {
      facets: {
        global: {},
        aggs: {}
      },
      byWork: {
        terms: {
          field: 'workUri',
          order: { top: 'desc' },
          size: Constants.maxSearchResults
        },
        aggs: {
          publications: {
            top_hits: {
              size: 1
            }
          },
          top: {
            max: {
              script: {
                lang: 'expression',
                inline: '_score'
              }
            }
          }
        }
      },
      workCount: {
        cardinality: {
          field: 'workUri'
        }
      }
    }
  }
}

function initSimpleQuery (query) {
  return {
    filtered: {
      filter: {
        bool: {
          must: []
        }
      },
      query: {
        bool: {
          should: [
            {
              simple_query_string: {
                query: query,
                default_operator: 'and',
                fields: [ 'mainTitle^2', 'partTitle', 'subject', 'author', 'agents', 'genre', 'series', 'format', 'mt' ]
              }
            },
            {
              multi_match: {
                query: query,
                fuzziness: 'AUTO',
                type: 'cross_fields',
                fields: [ 'mainTitle^2', 'partTitle', 'subject', 'author', 'agents', 'genre', 'series', 'format', 'mt' ]
              }
            }
          ],
          must: []
        }
      }
    }
  }
}

function initAdvancedQuery (query) {
  return {
    filtered: {
      filter: {
        bool: {
          must: []
        }
      },
      query: {
        query_string: {
          query: query,
          default_operator: 'and'
        }
      }
    }
  }
}

function isAdvancedQuery (queryString) {
  return /[:+-^()"*]/.test(queryString)
}

function simpleSearchBuilder (queryString) {
  const query = initCommonQuery()
  query.query = initSimpleQuery(queryString)
  return query
}

function advancedSearchBuilder (queryString) {
  const query = initCommonQuery()
  query.query = initAdvancedQuery(queryString)
  return query
}

function parseFilters (filtersFromLocationQuery) {
  const filterableFields = Constants.filterableFields
  const filterBuckets = {}
  if (!Array.isArray(filtersFromLocationQuery)) {
    filtersFromLocationQuery = [ filtersFromLocationQuery ]
  }
  filtersFromLocationQuery.forEach(filter => { // ex filter: 'audience_juvenile'
    const split = filter.split('_')
    const filterableField = filterableFields[ split[ 0 ] ]
    const aggregation = filterableField.name
    if (!filterBuckets[ aggregation ]) {
      filterBuckets[ aggregation ] = []
    }
    const val = filterableField.prefix + filter.substring(`${split[ 0 ]}_`.length)
    filterBuckets[ aggregation ].push(val)
  })

  const filters = []
  Object.keys(filterBuckets).forEach(aggregation => {
    filters.push({ aggregation: aggregation, bucket: filterBuckets[ aggregation ] })
  })

  return filters
}

function createMust (field, terms) {
  return {
    terms: {
      [field]: terms
    }
  }
}

module.exports.buildQuery = function (urlQueryString) {
  const params = querystring.parse(urlQueryString)
  const queryString = params.query
  let elasticSearchQuery = {}
  if (isAdvancedQuery(queryString)) {
    elasticSearchQuery = advancedSearchBuilder(queryString)
  } else {
    elasticSearchQuery = simpleSearchBuilder(queryString)
  }
  const filters = parseFilters(params.filter || [])
  const musts = {}
  filters.forEach(filter => {
    const aggregation = filter.aggregation
    const must = createMust(aggregation, filter.bucket)
    musts[ aggregation ] = must
  })

  Object.keys(musts).forEach(aggregation => {
    elasticSearchQuery.query.filtered.filter.bool.must.push(musts[ aggregation ])
  })

  Object.keys(Constants.filterableFields).forEach(key => {
    const field = Constants.filterableFields[ key ]
    const fieldName = field.name
    elasticSearchQuery.aggs.facets.aggs[ fieldName ] = {
      filter: {
        bool: Object.assign({}, elasticSearchQuery.query.filtered.query.bool || { filter: [ elasticSearchQuery.query.filtered.query ] })
      },
      aggs: {
        [fieldName]: {
          terms: {
            field: fieldName,
            size: 0
          }
        }
      }
    }

    const aggregationMusts = []
    Object.keys(musts).forEach(aggregation => {
      const must = musts[ aggregation ]
      if (aggregation !== fieldName) {
        aggregationMusts.push(must)
      }
    })
    elasticSearchQuery.aggs.facets.aggs[ fieldName ].filter.bool.must = aggregationMusts
  })

  return elasticSearchQuery
}
<|endoftext|>


<commit_before>'use strict';

import React, {
	Component,
	PropTypes,
} from 'react';
import {
	Animated,
	PanResponder,
	Platform,
	StyleSheet,
	TouchableOpacity,
	View
} from 'react-native';

const DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD = 2;
const PREVIEW_OPEN_DELAY = 700;
const PREVIEW_CLOSE_DELAY = 300;

/**
 * Row that is generally used in a SwipeListView.
 * If you are rendering a SwipeRow explicitly you must pass the SwipeRow exactly two children.
 * The first will be rendered behind the second.
 * e.g.
  <SwipeRow>
      <View style={hiddenRowStyle} />
      <View style={visibleRowStyle} />
  </SwipeRow>
 */
class SwipeRow extends Component {

	constructor(props) {
		super(props);
		this.horizontalSwipeGestureBegan = false;
		this.swipeInitialX = null;
		this.parentScrollEnabled = true;
		this.ranPreview = false;
		this.state = {
			dimensionsSet: false,
			hiddenHeight: 0,
			hiddenWidth: 0,
			translateX: new Animated.Value(0)
		};
	}

	componentWillMount() {
		this._panResponder = PanResponder.create({
			onMoveShouldSetPanResponder: (e, gs) => this.handleOnMoveShouldSetPanResponder(e, gs),
			onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
			onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
			onPanResponderTerminate: (e, gs) => this.handlePanResponderEnd(e, gs),
			onShouldBlockNativeResponder: _ => false,
		});
	}

	getPreviewAnimation(toValue, delay) {
		return Animated.timing(
			this.state.translateX,
			{ duration: this.props.previewDuration, toValue, delay }
		);
	}

	onContentLayout(e) {
		this.setState({
			dimensionsSet: !this.props.recalculateHiddenLayout,
			hiddenHeight: e.nativeEvent.layout.height,
			hiddenWidth: e.nativeEvent.layout.width,
		});

		if (this.props.preview && !this.ranPreview) {
			this.ranPreview = true;
			let previewOpenValue = this.props.previewOpenValue || this.props.rightOpenValue * 0.5;
			this.getPreviewAnimation(previewOpenValue, PREVIEW_OPEN_DELAY)
			.start( _ => {
				this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
			});
		}
	}

	onRowPress() {
		if (this.props.onRowPress) {
			this.props.onRowPress();
		} else {
			if (this.props.closeOnRowPress) {
				this.closeRow();
			}
		}
	}

	handleOnMoveShouldSetPanResponder(e, gs) {
		const { dx } = gs;
		return Math.abs(dx) > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD;
	}

	handlePanResponderMove(e, gestureState) {
		const { dx, dy } = gestureState;
		const absDx = Math.abs(dx);
		const absDy = Math.abs(dy);

		// this check may not be necessary because we don't capture the move until we pass the threshold
		// just being extra safe here
		if (absDx > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD || absDy > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD) {
			// we have enough to determine direction
			if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
				// user is moving vertically, do nothing, listView will handle
				return;
			}

			// user is moving horizontally
			if (this.parentScrollEnabled) {
				// disable scrolling on the listView parent
				this.parentScrollEnabled = false;
				this.props.setScrollEnabled && this.props.setScrollEnabled(false);
			}

			if (this.swipeInitialX === null) {
				// set tranlateX value when user started swiping
				this.swipeInitialX = this.state.translateX._value
			}
			this.horizontalSwipeGestureBegan = true;

			let newDX = this.swipeInitialX + dx;
			if (this.props.disableLeftSwipe  && newDX < 0) { newDX = 0; }
			if (this.props.disableRightSwipe && newDX > 0) { newDX = 0; }

			if (this.props.stopLeftSwipe && newDX > this.props.stopLeftSwipe) { newDX = this.props.stopLeftSwipe; }
			if (this.props.stopRightSwipe && newDX < this.props.stopRightSwipe) { newDX = this.props.stopRightSwipe; }

			this.setState({
				translateX: new Animated.Value(newDX)
			});

		}
	}

	handlePanResponderEnd(e, gestureState) {
		// re-enable scrolling on listView parent
		if (!this.parentScrollEnabled) {
			this.parentScrollEnabled = true;
			this.props.setScrollEnabled && this.props.setScrollEnabled(true);
		}

		// finish up the animation
		let toValue = 0;
		if (this.state.translateX._value >= 0) {
			// trying to open right
			if (this.state.translateX._value > this.props.leftOpenValue / 2) {
				// we're more than halfway
				toValue = this.props.leftOpenValue;
			}
		} else {
			// trying to open left
			if (this.state.translateX._value < this.props.rightOpenValue / 2) {
				// we're more than halfway
				toValue = this.props.rightOpenValue
			}
		}

		this.manuallySwipeRow(toValue);
	}

	/*
	 * This method is called by SwipeListView
	 */
	closeRow() {
		this.manuallySwipeRow(0);
	}

	manuallySwipeRow(toValue) {
		Animated.spring(this.state.translateX,
			{
				toValue,
				friction: this.props.friction,
				tension: this.props.tension
			}
		).start();

		if (toValue === 0) {
			this.props.onRowClose && this.props.onRowClose();
		} else {
			this.props.onRowOpen && this.props.onRowOpen();
		}

		// reset everything
		this.swipeInitialX = null;
		this.horizontalSwipeGestureBegan = false;
	}

	renderVisibleContent() {
		// handle touchables
		const onPress = this.props.children[1].props.onPress;

		if (onPress) {
			const newOnPress = _ => {
				this.onRowPress();
				onPress();
			}
			return React.cloneElement(
				this.props.children[1],
				{
					...this.props.children[1].props,
					onPress: newOnPress
				}
			);
		}

		return (
			<TouchableOpacity
				activeOpacity={1}
				onPress={ _ => this.onRowPress() }
			>
				{this.props.children[1]}
			</TouchableOpacity>
		)

	}

	renderRowContent() {
		// We do this annoying if statement for performance.
		// We don't want the onLayout func to run after it runs once.
		if (this.state.dimensionsSet) {
			return (
				<Animated.View
					{...this._panResponder.panHandlers}
					style={{
						transform: [
							{translateX: this.state.translateX}
						]
					}}
				>
					{this.renderVisibleContent()}
				</Animated.View>
			);
		} else {
			return (
				<Animated.View
					{...this._panResponder.panHandlers}
					onLayout={ (e) => this.onContentLayout(e) }
					style={{
						transform: [
							{translateX: this.state.translateX}
						]
					}}
				>
					{this.renderVisibleContent()}
				</Animated.View>
			);
		}
	}

	render() {
		return (
			<View style={this.props.style ? this.props.style : styles.container}>
				<View style={[
					styles.hidden,
					{
						height: this.state.hiddenHeight,
						width: this.state.hiddenWidth,
					}
				]}>
					{this.props.children[0]}
				</View>
				{this.renderRowContent()}
			</View>
		);
	}

}

const styles = StyleSheet.create({
	container: {
		// As of RN 0.29 flex: 1 is causing all rows to be the same height
		// flex: 1
	},
	hidden: {
		bottom: 0,
		left: 0,
		overflow: 'hidden',
		position: 'absolute',
		right: 0,
		top: 0,
	},
});

SwipeRow.propTypes = {
	/**
	 * Used by the SwipeListView to close rows on scroll events.
	 * You shouldn't need to use this prop explicitly.
	 */
	setScrollEnabled: PropTypes.func,
	/**
	 * Called when a swipe row is animating open. Used by the SwipeListView
	 * to keep references to open rows.
	 */
	onRowOpen: PropTypes.func,
	/**
	 * TranslateX value for opening the row to the left (positive number)
	 */
	leftOpenValue: PropTypes.number,
	/**
	 * TranslateX value for opening the row to the right (negative number)
	 */
	rightOpenValue: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the left (positive number)
	 */
	stopLeftSwipe: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the right (negative number)
	 */
	stopRightSwipe: PropTypes.number,
	/**
	 * Friction for the open / close animation
	 */
	friction: PropTypes.number,
	/**
	 * Tension for the open / close animation
	 */
	tension: PropTypes.number,
	/**
	 * Should the row be closed when it is tapped
	 */
	closeOnRowPress: PropTypes.bool,
	/**
	 * Disable ability to swipe the row left
	 */
	disableLeftSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe the row right
	 */
	disableRightSwipe: PropTypes.bool,
	/**
	 * Enable hidden row onLayout calculations to run always
	 */
	recalculateHiddenLayout: PropTypes.bool,
	/**
	 * Called when a swipe row is animating closed
	 */
	onRowClose: PropTypes.func,
	/**
	 * Styles for the parent wrapper View of the SwipeRow
	 */
	style: PropTypes.object,
	/**
	 * Should the row do a slide out preview to show that it is swipeable
	 */
	preview: PropTypes.bool,
	/**
	 * Duration of the slide out preview animation
	 */
	previewDuration: PropTypes.number,
	/**
	 * TranslateX value for the slide out preview animation
	 * Default: 0.5 * props.rightOpenValue
	 */
	previewOpenValue: PropTypes.number
};

SwipeRow.defaultProps = {
	leftOpenValue: 0,
	rightOpenValue: 0,
	closeOnRowPress: true,
	disableLeftSwipe: false,
	disableRightSwipe: false,
	recalculateHiddenLayout: false,
	preview: false,
	previewDuration: 300
};

export default SwipeRow;
<commit_msg>Pass new translateX value through SwipeRow onRowOpen.<commit_after>'use strict';

import React, {
	Component,
	PropTypes,
} from 'react';
import {
	Animated,
	PanResponder,
	Platform,
	StyleSheet,
	TouchableOpacity,
	View
} from 'react-native';

const DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD = 2;
const PREVIEW_OPEN_DELAY = 700;
const PREVIEW_CLOSE_DELAY = 300;

/**
 * Row that is generally used in a SwipeListView.
 * If you are rendering a SwipeRow explicitly you must pass the SwipeRow exactly two children.
 * The first will be rendered behind the second.
 * e.g.
  <SwipeRow>
      <View style={hiddenRowStyle} />
      <View style={visibleRowStyle} />
  </SwipeRow>
 */
class SwipeRow extends Component {

	constructor(props) {
		super(props);
		this.horizontalSwipeGestureBegan = false;
		this.swipeInitialX = null;
		this.parentScrollEnabled = true;
		this.ranPreview = false;
		this.state = {
			dimensionsSet: false,
			hiddenHeight: 0,
			hiddenWidth: 0,
			translateX: new Animated.Value(0)
		};
	}

	componentWillMount() {
		this._panResponder = PanResponder.create({
			onMoveShouldSetPanResponder: (e, gs) => this.handleOnMoveShouldSetPanResponder(e, gs),
			onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
			onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
			onPanResponderTerminate: (e, gs) => this.handlePanResponderEnd(e, gs),
			onShouldBlockNativeResponder: _ => false,
		});
	}

	getPreviewAnimation(toValue, delay) {
		return Animated.timing(
			this.state.translateX,
			{ duration: this.props.previewDuration, toValue, delay }
		);
	}

	onContentLayout(e) {
		this.setState({
			dimensionsSet: !this.props.recalculateHiddenLayout,
			hiddenHeight: e.nativeEvent.layout.height,
			hiddenWidth: e.nativeEvent.layout.width,
		});

		if (this.props.preview && !this.ranPreview) {
			this.ranPreview = true;
			let previewOpenValue = this.props.previewOpenValue || this.props.rightOpenValue * 0.5;
			this.getPreviewAnimation(previewOpenValue, PREVIEW_OPEN_DELAY)
			.start( _ => {
				this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
			});
		}
	}

	onRowPress() {
		if (this.props.onRowPress) {
			this.props.onRowPress();
		} else {
			if (this.props.closeOnRowPress) {
				this.closeRow();
			}
		}
	}

	handleOnMoveShouldSetPanResponder(e, gs) {
		const { dx } = gs;
		return Math.abs(dx) > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD;
	}

	handlePanResponderMove(e, gestureState) {
		const { dx, dy } = gestureState;
		const absDx = Math.abs(dx);
		const absDy = Math.abs(dy);

		// this check may not be necessary because we don't capture the move until we pass the threshold
		// just being extra safe here
		if (absDx > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD || absDy > DIRECTIONAL_DISTANCE_CHANGE_THRESHOLD) {
			// we have enough to determine direction
			if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
				// user is moving vertically, do nothing, listView will handle
				return;
			}

			// user is moving horizontally
			if (this.parentScrollEnabled) {
				// disable scrolling on the listView parent
				this.parentScrollEnabled = false;
				this.props.setScrollEnabled && this.props.setScrollEnabled(false);
			}

			if (this.swipeInitialX === null) {
				// set tranlateX value when user started swiping
				this.swipeInitialX = this.state.translateX._value
			}
			this.horizontalSwipeGestureBegan = true;

			let newDX = this.swipeInitialX + dx;
			if (this.props.disableLeftSwipe  && newDX < 0) { newDX = 0; }
			if (this.props.disableRightSwipe && newDX > 0) { newDX = 0; }

			if (this.props.stopLeftSwipe && newDX > this.props.stopLeftSwipe) { newDX = this.props.stopLeftSwipe; }
			if (this.props.stopRightSwipe && newDX < this.props.stopRightSwipe) { newDX = this.props.stopRightSwipe; }

			this.setState({
				translateX: new Animated.Value(newDX)
			});

		}
	}

	handlePanResponderEnd(e, gestureState) {
		// re-enable scrolling on listView parent
		if (!this.parentScrollEnabled) {
			this.parentScrollEnabled = true;
			this.props.setScrollEnabled && this.props.setScrollEnabled(true);
		}

		// finish up the animation
		let toValue = 0;
		if (this.state.translateX._value >= 0) {
			// trying to open right
			if (this.state.translateX._value > this.props.leftOpenValue / 2) {
				// we're more than halfway
				toValue = this.props.leftOpenValue;
			}
		} else {
			// trying to open left
			if (this.state.translateX._value < this.props.rightOpenValue / 2) {
				// we're more than halfway
				toValue = this.props.rightOpenValue
			}
		}

		this.manuallySwipeRow(toValue);
	}

	/*
	 * This method is called by SwipeListView
	 */
	closeRow() {
		this.manuallySwipeRow(0);
	}

	manuallySwipeRow(toValue) {
		Animated.spring(this.state.translateX,
			{
				toValue,
				friction: this.props.friction,
				tension: this.props.tension
			}
		).start();

		if (toValue === 0) {
			this.props.onRowClose && this.props.onRowClose();
		} else {
			this.props.onRowOpen && this.props.onRowOpen(toValue);
		}

		// reset everything
		this.swipeInitialX = null;
		this.horizontalSwipeGestureBegan = false;
	}

	renderVisibleContent() {
		// handle touchables
		const onPress = this.props.children[1].props.onPress;

		if (onPress) {
			const newOnPress = _ => {
				this.onRowPress();
				onPress();
			}
			return React.cloneElement(
				this.props.children[1],
				{
					...this.props.children[1].props,
					onPress: newOnPress
				}
			);
		}

		return (
			<TouchableOpacity
				activeOpacity={1}
				onPress={ _ => this.onRowPress() }
			>
				{this.props.children[1]}
			</TouchableOpacity>
		)

	}

	renderRowContent() {
		// We do this annoying if statement for performance.
		// We don't want the onLayout func to run after it runs once.
		if (this.state.dimensionsSet) {
			return (
				<Animated.View
					{...this._panResponder.panHandlers}
					style={{
						transform: [
							{translateX: this.state.translateX}
						]
					}}
				>
					{this.renderVisibleContent()}
				</Animated.View>
			);
		} else {
			return (
				<Animated.View
					{...this._panResponder.panHandlers}
					onLayout={ (e) => this.onContentLayout(e) }
					style={{
						transform: [
							{translateX: this.state.translateX}
						]
					}}
				>
					{this.renderVisibleContent()}
				</Animated.View>
			);
		}
	}

	render() {
		return (
			<View style={this.props.style ? this.props.style : styles.container}>
				<View style={[
					styles.hidden,
					{
						height: this.state.hiddenHeight,
						width: this.state.hiddenWidth,
					}
				]}>
					{this.props.children[0]}
				</View>
				{this.renderRowContent()}
			</View>
		);
	}

}

const styles = StyleSheet.create({
	container: {
		// As of RN 0.29 flex: 1 is causing all rows to be the same height
		// flex: 1
	},
	hidden: {
		bottom: 0,
		left: 0,
		overflow: 'hidden',
		position: 'absolute',
		right: 0,
		top: 0,
	},
});

SwipeRow.propTypes = {
	/**
	 * Used by the SwipeListView to close rows on scroll events.
	 * You shouldn't need to use this prop explicitly.
	 */
	setScrollEnabled: PropTypes.func,
	/**
	 * Called when a swipe row is animating open. Used by the SwipeListView
	 * to keep references to open rows.
	 */
	onRowOpen: PropTypes.func,
	/**
	 * TranslateX value for opening the row to the left (positive number)
	 */
	leftOpenValue: PropTypes.number,
	/**
	 * TranslateX value for opening the row to the right (negative number)
	 */
	rightOpenValue: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the left (positive number)
	 */
	stopLeftSwipe: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the right (negative number)
	 */
	stopRightSwipe: PropTypes.number,
	/**
	 * Friction for the open / close animation
	 */
	friction: PropTypes.number,
	/**
	 * Tension for the open / close animation
	 */
	tension: PropTypes.number,
	/**
	 * Should the row be closed when it is tapped
	 */
	closeOnRowPress: PropTypes.bool,
	/**
	 * Disable ability to swipe the row left
	 */
	disableLeftSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe the row right
	 */
	disableRightSwipe: PropTypes.bool,
	/**
	 * Enable hidden row onLayout calculations to run always
	 */
	recalculateHiddenLayout: PropTypes.bool,
	/**
	 * Called when a swipe row is animating closed
	 */
	onRowClose: PropTypes.func,
	/**
	 * Styles for the parent wrapper View of the SwipeRow
	 */
	style: PropTypes.object,
	/**
	 * Should the row do a slide out preview to show that it is swipeable
	 */
	preview: PropTypes.bool,
	/**
	 * Duration of the slide out preview animation
	 */
	previewDuration: PropTypes.number,
	/**
	 * TranslateX value for the slide out preview animation
	 * Default: 0.5 * props.rightOpenValue
	 */
	previewOpenValue: PropTypes.number
};

SwipeRow.defaultProps = {
	leftOpenValue: 0,
	rightOpenValue: 0,
	closeOnRowPress: true,
	disableLeftSwipe: false,
	disableRightSwipe: false,
	recalculateHiddenLayout: false,
	preview: false,
	previewDuration: 300
};

export default SwipeRow;
<|endoftext|>


<commit_before>import API from 'src/util/api';
import UI from 'src/util/ui';

import OCLE from './libs/OCLE';

const noop = () => {
  /* noop */
};

const defaultOptions = {
  onMolfileChanged: noop,
  calculateDiastereotopicID: false,
  maxDiastereotopicCalculationTime: 3000
};

class ExpandableMolecule {
  constructor(sample, options) {
    this.options = Object.assign({}, defaultOptions, options);
    this.sample = sample;
    this.molfile = String(
      this.sample.getChildSync(['$content', 'general', 'molfile']) || ''
    );
    this.idCode = OCLE.Molecule.fromMolfile(this.molfile).getIDCode();
    this.expandedHydrogens = false;
    this.jsmeEditionMode = false;
    this.calculateDiastereotopicID = this.options.calculateDiastereotopicID;
    this.maxDiastereotopicCalculationTime = this.options.maxDiastereotopicCalculationTime;

    this.onChange = (event) => {
      // us this really a modification ? or a loop event ...
      // need to compare former oclID with new oclID
      var newMolecule = OCLE.Molecule.fromMolfile(`${event.target}`);

      var oclID = newMolecule.getIDCodeAndCoordinates();
      if (oclID.idCode !== this.idCode) {
        this.idCode = oclID.idCode;
        this.molfile = `${event.target}`;
        this.sample.setChildSync(
          ['$content', 'general', 'molfile'],
          this.molfile
        );
        this.sample.setChildSync(['$content', 'general', 'ocl'], {
          value: oclID.idCode,
          coordinates: oclID.coordinates,
          index: newMolecule.getIndex()
        });
      }
      this.options.onMolfileChanged(this);
      this.updateHistory();
    };

    API.createData('editableMolfile', this.molfile).then((editableMolfile) => {
      this.editableMolfile = editableMolfile;
      this.bindChange();
      this.createViewVariable();
    });
  }

  bindChange() {
    this.editableMolfile.onChange(this.onChange);
  }

  unbindChange() {
    this.editableMolfile.unbindChange(this.onChange);
  }

  updateHistory() {
    let history = JSON.parse(localStorage.getItem('moleculesHistory') || '[]');
    let exists = false;
    let uuid = this.sample._id;
    let entry;
    for (entry of history) {
      if (entry.uuid === uuid) {
        exists = true;
        break;
      }
    }
    if (!exists) {
      entry = { uuid };
      history.push(entry);
    }
    entry.timestamp = Date.now();
    entry.idCode = this.idCode;
    entry.molfile = this.molfile;
    if (this.sample.$id) {
      entry.id = Object.resurrect(this.sample.$id)
        .filter((a) => a)
        .join(' ');
    }
    // we sort by timestamp
    history = history.filter((entry) => entry.idCode !== 'd@');
    history.sort((a, b) => b.timestamp - a.timestamp);
    history.slice(0, 20);
    localStorage.setItem('moleculesHistory', JSON.stringify(history));
  }

  toggleJSMEEdition() {
    this.jsmeEditionMode = !this.jsmeEditionMode;
    this.expandedHydrogens = false;
    let options = {
      prefs: []
    };

    if (this.jsmeEditionMode) {
      options.prefs.push('nodepict');
      API.createData('editableMolfile', this.molfile);
    } else {
      options.prefs.push('depict');
      this.createViewVariable();
    }
    API.doAction('setJSMEOptions', options);
  }

  setExpandedHydrogens() {
    if (this.jsmeEditionMode) {
      this.toggleJSMEEdition();
    } else {
      this.expandedHydrogens = !this.expandedHydrogens;
    }
    this.createViewVariable();
  }

  /*
    We create the view variable with or without expanded hydrogens
     */
  createViewVariable() {
    var molecule = OCLE.Molecule.fromMolfile(this.molfile);
    let calculateDiastereotopicID = this.calculateDiastereotopicID;
    if (calculateDiastereotopicID) {
      // is it reasonnable to calculate the DiastereotopicID. We check the time it will take

      var start = Date.now();
      molecule.getCompactCopy().getIDCode();
      let expected = (Date.now() - start) * 4 * molecule.getAllAtoms();
      if (expected > this.maxDiastereotopicCalculationTime) {
        // eslint-disable-next-line no-console
        console.log(
          'The diastereotopic calculation is expected to last more than 3s. No way to assign molecule.',
          this.maxDiastereotopicCalculationTime
        );
        calculateDiastereotopicID = false;
      }
    }
    if (this.expandedHydrogens) {
      molecule.addImplicitHydrogens();
      let viewMolfileExpandedH = molecule.toVisualizerMolfile({
        diastereotopic: calculateDiastereotopicID
      });
      API.createData('viewMolfileExpandedH', viewMolfileExpandedH);
    } else {
      let viewMolfile = molecule.toVisualizerMolfile({
        heavyAtomHydrogen: true,
        diastereotopic: calculateDiastereotopicID
      });
      API.createData('viewMolfile', viewMolfile);
    }
  }

  setMolfile(molfile) {
    const editableMolfile = API.getData('editableMolfile');
    if (editableMolfile) editableMolfile.setValue(molfile || '');
  }

  async loadMolfileFromHistory() {
    let history = JSON.parse(localStorage.getItem('moleculesHistory') || '[]');
    let molecule = await UI.choose(history, {
      autoSelect: false,
      noConfirmation: true,
      returnRow: true,
      dialog: {
        width: 600,
        height: 800
      },
      columns: [
        {
          id: 'id',
          name: 'Reference',
          jpath: ['id'],
          maxWidth: 100
        },
        {
          id: 'molfile',
          name: 'Structure',
          jpath: ['molfile'],
          rendererOptions: {
            forceType: 'mol2d'
          }
        }
      ],
      idField: 'uuid',
      slick: {
        rowHeight: 140
      }
    });
    if (molecule) {
      this.setMolfile(String(molecule.molfile));
    }
  }

  handleAction(action) {
    if (!action) return false;
    switch (action.name) {
      case 'toggleJSMEEdition':
        this.toggleJSMEEdition(!this.jsmeEditionMode);
        break;
      case 'clearMolfile':
        var molfile = API.getData('editableMolfile');
        molfile.setValue('');
        break;
      case 'swapHydrogens':
        this.setExpandedHydrogens();
        break;
      case 'loadMolfileFromHistory':
        this.loadMolfileFromHistory();
        break;
      default:
        return false;
    }
    return true;
  }
}

module.exports = ExpandableMolecule;
<commit_msg>fix prev commit<commit_after>import API from 'src/util/api';
import UI from 'src/util/ui';

import OCLE from './libs/OCLE';

const noop = () => {
  /* noop */
};

const defaultOptions = {
  onMolfileChanged: noop,
  calculateDiastereotopicID: false,
  maxDiastereotopicCalculationTime: 3000
};

class ExpandableMolecule {
  constructor(sample, options) {
    this.options = Object.assign({}, defaultOptions, options);
    this.sample = sample;
    this.molfile = String(
      this.sample.getChildSync(['$content', 'general', 'molfile']) || ''
    );
    this.idCode = OCLE.Molecule.fromMolfile(this.molfile).getIDCode();
    this.expandedHydrogens = false;
    this.jsmeEditionMode = false;
    this.calculateDiastereotopicID = this.options.calculateDiastereotopicID;
    this.maxDiastereotopicCalculationTime = this.options.maxDiastereotopicCalculationTime;

    this.onChange = (event) => {
      // us this really a modification ? or a loop event ...
      // need to compare former oclID with new oclID
      var newMolecule = OCLE.Molecule.fromMolfile(`${event.target}`);

      var oclID = newMolecule.getIDCodeAndCoordinates();
      if (oclID.idCode !== this.idCode) {
        this.idCode = oclID.idCode;
        this.molfile = `${event.target}`;
        this.sample.setChildSync(
          ['$content', 'general', 'molfile'],
          this.molfile
        );
        this.sample.setChildSync(['$content', 'general', 'ocl'], {
          value: oclID.idCode,
          coordinates: oclID.coordinates,
          index: newMolecule.getIndex()
        });
      }
      this.options.onMolfileChanged(this);
      this.updateHistory();
    };

    API.createData('editableMolfile', this.molfile).then((editableMolfile) => {
      this.editableMolfile = editableMolfile;
      this.bindChange();
      this.createViewVariable();
    });
  }

  bindChange() {
    this.editableMolfile.onChange(this.onChange);
  }

  unbindChange() {
    this.editableMolfile.unbindChange(this.onChange);
  }

  updateHistory() {
    let history = JSON.parse(localStorage.getItem('moleculesHistory') || '[]');
    let exists = false;
    let uuid = this.sample._id;
    let entry;
    for (entry of history) {
      if (entry.uuid === uuid) {
        exists = true;
        break;
      }
    }
    if (!exists) {
      entry = { uuid };
      history.push(entry);
    }
    entry.timestamp = Date.now();
    entry.idCode = this.idCode;
    entry.molfile = this.molfile;
    if (this.sample.$id) {
      entry.id = DataObject.resurrect(this.sample.$id)
        .filter((a) => a)
        .join(' ');
    }
    // we sort by timestamp
    history = history.filter((entry) => entry.idCode !== 'd@');
    history.sort((a, b) => b.timestamp - a.timestamp);
    history.slice(0, 20);
    localStorage.setItem('moleculesHistory', JSON.stringify(history));
  }

  toggleJSMEEdition() {
    this.jsmeEditionMode = !this.jsmeEditionMode;
    this.expandedHydrogens = false;
    let options = {
      prefs: []
    };

    if (this.jsmeEditionMode) {
      options.prefs.push('nodepict');
      API.createData('editableMolfile', this.molfile);
    } else {
      options.prefs.push('depict');
      this.createViewVariable();
    }
    API.doAction('setJSMEOptions', options);
  }

  setExpandedHydrogens() {
    if (this.jsmeEditionMode) {
      this.toggleJSMEEdition();
    } else {
      this.expandedHydrogens = !this.expandedHydrogens;
    }
    this.createViewVariable();
  }

  /*
    We create the view variable with or without expanded hydrogens
     */
  createViewVariable() {
    var molecule = OCLE.Molecule.fromMolfile(this.molfile);
    let calculateDiastereotopicID = this.calculateDiastereotopicID;
    if (calculateDiastereotopicID) {
      // is it reasonnable to calculate the DiastereotopicID. We check the time it will take

      var start = Date.now();
      molecule.getCompactCopy().getIDCode();
      let expected = (Date.now() - start) * 4 * molecule.getAllAtoms();
      if (expected > this.maxDiastereotopicCalculationTime) {
        // eslint-disable-next-line no-console
        console.log(
          'The diastereotopic calculation is expected to last more than 3s. No way to assign molecule.',
          this.maxDiastereotopicCalculationTime
        );
        calculateDiastereotopicID = false;
      }
    }
    if (this.expandedHydrogens) {
      molecule.addImplicitHydrogens();
      let viewMolfileExpandedH = molecule.toVisualizerMolfile({
        diastereotopic: calculateDiastereotopicID
      });
      API.createData('viewMolfileExpandedH', viewMolfileExpandedH);
    } else {
      let viewMolfile = molecule.toVisualizerMolfile({
        heavyAtomHydrogen: true,
        diastereotopic: calculateDiastereotopicID
      });
      API.createData('viewMolfile', viewMolfile);
    }
  }

  setMolfile(molfile) {
    const editableMolfile = API.getData('editableMolfile');
    if (editableMolfile) editableMolfile.setValue(molfile || '');
  }

  async loadMolfileFromHistory() {
    let history = JSON.parse(localStorage.getItem('moleculesHistory') || '[]');
    let molecule = await UI.choose(history, {
      autoSelect: false,
      noConfirmation: true,
      returnRow: true,
      dialog: {
        width: 600,
        height: 800
      },
      columns: [
        {
          id: 'id',
          name: 'Reference',
          jpath: ['id'],
          maxWidth: 100
        },
        {
          id: 'molfile',
          name: 'Structure',
          jpath: ['molfile'],
          rendererOptions: {
            forceType: 'mol2d'
          }
        }
      ],
      idField: 'uuid',
      slick: {
        rowHeight: 140
      }
    });
    if (molecule) {
      this.setMolfile(String(molecule.molfile));
    }
  }

  handleAction(action) {
    if (!action) return false;
    switch (action.name) {
      case 'toggleJSMEEdition':
        this.toggleJSMEEdition(!this.jsmeEditionMode);
        break;
      case 'clearMolfile':
        var molfile = API.getData('editableMolfile');
        molfile.setValue('');
        break;
      case 'swapHydrogens':
        this.setExpandedHydrogens();
        break;
      case 'loadMolfileFromHistory':
        this.loadMolfileFromHistory();
        break;
      default:
        return false;
    }
    return true;
  }
}

module.exports = ExpandableMolecule;
<|endoftext|>


<commit_before>const crypto = require('crypto');
const winston = require('winston');

module.exports = function(pool) {
  const queries = {
    findGameQuery: 'SELECT id FROM games WHERE hash = decode($1, \'hex\') LIMIT 1',
    createGameQuery: 'INSERT INTO games (display_name, internal_name, hash) VALUES ($1, $2, $3) RETURNING id',
    createNewSessionQuery: 'INSERT INTO game_sessions (user_id, game_id, session_start, state) VALUES ($1, $2, $3, $4) RETURNING id',
    sessionDiscoverQuery: 'SELECT MAX(id) as id FROM game_sessions WHERE user_id = $1 AND game_id = $2 AND state = \'in_progress\' LIMIT 1',
    sessionEndQuery: 'UPDATE game_sessions SET state = \'completed\', session_end = $1 WHERE id = $2 RETURNING id',
    gameStatisticsQuery: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true GROUP BY display_name ORDER BY time DESC LIMIT $1',
    gameStatisticsQueryString: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND LOWER(display_name) ~ LOWER($1) GROUP BY display_name ORDER BY time DESC LIMIT $2;',
    userGameStatisticsQuery: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND user_id = $1 GROUP BY display_name ORDER BY time DESC LIMIT $2',
    userGameStatisticsQueryString: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND user_id = $1 AND LOWER(display_name) ~ LOWER($2) GROUP BY display_name ORDER BY time DESC LIMIT $3;',
    gameGraphQuery: 'SELECT DATE(session_end), JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND display_name IN (SELECT display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true GROUP BY display_name ORDER BY JUSTIFY_INTERVAL(SUM(session_end - session_start)) DESC LIMIT $1) GROUP BY DATE(session_end), display_name ORDER BY DATE LIMIT ($1 * $2);',
    gameGraphQueryString: 'SELECT DATE(session_end), JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND display_name IN (SELECT display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND LOWER(display_name) ~ LOWER($2) GROUP BY display_name ORDER BY JUSTIFY_INTERVAL(SUM(session_end - session_start)) DESC LIMIT $1) GROUP BY DATE(session_end), display_name ORDER BY DATE LIMIT ($1 * $3);',
  };

  return {
    findOrCreateGame: async function(gameName) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        // compute internal name and hash for the game
        const internalName = gameName.replace(/[ ''\-!?\.]|\(.+\)/g, '').toLowerCase();
        const gameHash = crypto.createHash('sha512').update(internalName).digest('hex');
        const searchResult = await client.query(queries['findGameQuery'], [gameHash]);
        if (searchResult.rowCount == 1) {
          // game was found
          winston.log('debug', 'Found game ID %s for game %s', searchResult.rows[0].id, gameName);
          await client.query('COMMIT');
          return searchResult.rows[0].id;
        } else {
          const createResult = await client.query(queries['createGameQuery'], [gameName, internalName, gameHash]);
          winston.log('debug', 'Created new game ID %s for game %s', createResult.rows[0].id, gameName);
          await client.query('COMMIT');
          return createResult.rows[0].id;
        }
      } catch (e) {
        winston.log('error', 'Failed to find or create game', e);
        await client.query('ROLLBACK');
      } finally {
        client.release();
      }
    },

    createNewSession: async function(userId, gameId) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        // before just blindly opening a new session, check to see if the user already has a session for the same game.
        // if they do, don't create a new session as it'll gum up the database
        // if they don't, then create a new session for them
        const sessionSearch = await client.query('SELECT MAX(id) as id FROM game_sessions WHERE user_id=$1 AND game_id=$2 AND state=\'in_progress\'', [userId, gameId]);
        let sessionId = sessionSearch.rows[0].id;
        if (sessionId) { // they already have a session
          winston.log('debug', 'Continuing dangling session %s for user %s playing %s', sessionSearch.rows[0].id, userId, gameId);
        } else { // they don't have a session for this game, start a new one
          const values = [userId, gameId, new Date(), 'in_progress'];
          const createResult = await client.query(queries['createNewSessionQuery'], values);
          winston.log('debug', 'Created new session (%s) for user %s playing %s', createResult.rows[0].id, userId, gameId);
          sessionId = createResult.rows[0].id;
        }
        await client.query('COMMIT');
        return sessionId;
      } catch (e) {
        await client.query('ROLLBACK');
        winston.log('error', 'Failed to begin session.', e);
      } finally {
        client.release();
      }
    },

    endSession: async function(userId, gameId) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        const discoverResult = await client.query(queries['sessionDiscoverQuery'], [userId, gameId]);
        const sessionId = discoverResult.rows[0].id;
        if (!sessionId) {
          winston.log('debug', 'No session found for user %s playing %s', userId, gameId);
          return;
        }
        await client.query(queries['sessionEndQuery'], [new Date(), sessionId]);
        await client.query('COMMIT');
        winston.log('debug', 'Ended session (%s) for user %s playing %s', sessionId, userId, gameId);
        return;
      } catch (e) {
        await client.query('ROLLBACK');
        winston.log('error', 'Failed to end session.', e);
      } finally {
        client.release();
      }
    },

    getGameStatistics: async function(limit) {
      const selectResult = await pool.query(queries['gameStatisticsQuery'], [limit]);
      return selectResult.rows;
    },

    getStatisticsForGame: async function(game, limit) {
      const selectResult = await pool.query(queries['gameStatisticsQueryString'], [game + '*', limit]);
      return selectResult.rows;
    },

    getUserGameStatistics: async function(userId, limit) {
      const selectResult = await pool.query(queries['userGameStatisticsQuery'], [userId, limit]);
      return selectResult.rows;
    },

    getUserStatisticsForGame: async function(user, game, limit) {
      const selectResult = await pool.query(queries['userGameStatisticsQueryString'], [user, game + '*', limit]);
      return selectResult.rows;
    },

    getGameGraph: async function(limit, days) {
      const selectResult = await pool.query(queries['gameGraphQuery'], [limit, days]);
      return selectResult.rows;
    },

    getGameGraphForGame: async function(limit, game, days) {
      const selectResult = await pool.query(queries['gameGraphQueryString'], [limit, game, days]);
      return selectResult.rows;
    },
  };
};
<commit_msg>Don't bother using hashes, just use internal name<commit_after>const winston = require('winston');

module.exports = function(pool) {
  const queries = {
    findGameQuery: 'SELECT id FROM games WHERE internal_name = $1 LIMIT 1',
    createGameQuery: 'INSERT INTO games (display_name, internal_name) VALUES ($1, $2) RETURNING id',
    createNewSessionQuery: 'INSERT INTO game_sessions (user_id, game_id, session_start, state) VALUES ($1, $2, $3, $4) RETURNING id',
    sessionDiscoverQuery: 'SELECT MAX(id) as id FROM game_sessions WHERE user_id = $1 AND game_id = $2 AND state = \'in_progress\' LIMIT 1',
    sessionEndQuery: 'UPDATE game_sessions SET state = \'completed\', session_end = $1 WHERE id = $2 RETURNING id',
    gameStatisticsQuery: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true GROUP BY display_name ORDER BY time DESC LIMIT $1',
    gameStatisticsQueryString: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND LOWER(display_name) ~ LOWER($1) GROUP BY display_name ORDER BY time DESC LIMIT $2;',
    userGameStatisticsQuery: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND user_id = $1 GROUP BY display_name ORDER BY time DESC LIMIT $2',
    userGameStatisticsQueryString: 'SELECT JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND user_id = $1 AND LOWER(display_name) ~ LOWER($2) GROUP BY display_name ORDER BY time DESC LIMIT $3;',
    gameGraphQuery: 'SELECT DATE(session_end), JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND display_name IN (SELECT display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true GROUP BY display_name ORDER BY JUSTIFY_INTERVAL(SUM(session_end - session_start)) DESC LIMIT $1) GROUP BY DATE(session_end), display_name ORDER BY DATE LIMIT ($1 * $2);',
    gameGraphQueryString: 'SELECT DATE(session_end), JUSTIFY_INTERVAL(SUM(session_end - session_start)) AS time, display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND display_name IN (SELECT display_name FROM game_sessions INNER JOIN games ON game_sessions.game_id = games.id WHERE state = \'completed\' AND visible = true AND LOWER(display_name) ~ LOWER($2) GROUP BY display_name ORDER BY JUSTIFY_INTERVAL(SUM(session_end - session_start)) DESC LIMIT $1) GROUP BY DATE(session_end), display_name ORDER BY DATE LIMIT ($1 * $3);',
  };

  return {
    findOrCreateGame: async function(gameName) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        // compute internal name and hash for the game
        const internalName = gameName.replace(/[ ''\-!?\.]|\(.+\)/g, '').toLowerCase();
        const searchResult = await client.query(queries['findGameQuery'], [internalName]);
        if (searchResult.rowCount == 1) {
          // game was found
          winston.log('debug', 'Found game ID %s for game %s', searchResult.rows[0].id, gameName);
          await client.query('COMMIT');
          return searchResult.rows[0].id;
        } else {
          const createResult = await client.query(queries['createGameQuery'], [gameName, internalName]);
          winston.log('debug', 'Created new game ID %s for game %s', createResult.rows[0].id, gameName);
          await client.query('COMMIT');
          return createResult.rows[0].id;
        }
      } catch (e) {
        winston.log('error', 'Failed to find or create game', e);
        await client.query('ROLLBACK');
      } finally {
        client.release();
      }
    },

    createNewSession: async function(userId, gameId) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        // before just blindly opening a new session, check to see if the user already has a session for the same game.
        // if they do, don't create a new session as it'll gum up the database
        // if they don't, then create a new session for them
        const sessionSearch = await client.query('SELECT MAX(id) as id FROM game_sessions WHERE user_id=$1 AND game_id=$2 AND state=\'in_progress\'', [userId, gameId]);
        let sessionId = sessionSearch.rows[0].id;
        if (sessionId) { // they already have a session
          winston.log('debug', 'Continuing dangling session %s for user %s playing %s', sessionSearch.rows[0].id, userId, gameId);
        } else { // they don't have a session for this game, start a new one
          const values = [userId, gameId, new Date(), 'in_progress'];
          const createResult = await client.query(queries['createNewSessionQuery'], values);
          winston.log('debug', 'Created new session (%s) for user %s playing %s', createResult.rows[0].id, userId, gameId);
          sessionId = createResult.rows[0].id;
        }
        await client.query('COMMIT');
        return sessionId;
      } catch (e) {
        await client.query('ROLLBACK');
        winston.log('error', 'Failed to begin session.', e);
      } finally {
        client.release();
      }
    },

    endSession: async function(userId, gameId) {
      const client = await pool.connect();
      try {
        await client.query('BEGIN TRANSACTION');
        const discoverResult = await client.query(queries['sessionDiscoverQuery'], [userId, gameId]);
        const sessionId = discoverResult.rows[0].id;
        if (!sessionId) {
          winston.log('debug', 'No session found for user %s playing %s', userId, gameId);
          return;
        }
        await client.query(queries['sessionEndQuery'], [new Date(), sessionId]);
        await client.query('COMMIT');
        winston.log('debug', 'Ended session (%s) for user %s playing %s', sessionId, userId, gameId);
        return;
      } catch (e) {
        await client.query('ROLLBACK');
        winston.log('error', 'Failed to end session.', e);
      } finally {
        client.release();
      }
    },

    getGameStatistics: async function(limit) {
      const selectResult = await pool.query(queries['gameStatisticsQuery'], [limit]);
      return selectResult.rows;
    },

    getStatisticsForGame: async function(game, limit) {
      const selectResult = await pool.query(queries['gameStatisticsQueryString'], [game + '*', limit]);
      return selectResult.rows;
    },

    getUserGameStatistics: async function(userId, limit) {
      const selectResult = await pool.query(queries['userGameStatisticsQuery'], [userId, limit]);
      return selectResult.rows;
    },

    getUserStatisticsForGame: async function(user, game, limit) {
      const selectResult = await pool.query(queries['userGameStatisticsQueryString'], [user, game + '*', limit]);
      return selectResult.rows;
    },

    getGameGraph: async function(limit, days) {
      const selectResult = await pool.query(queries['gameGraphQuery'], [limit, days]);
      return selectResult.rows;
    },

    getGameGraphForGame: async function(limit, game, days) {
      const selectResult = await pool.query(queries['gameGraphQueryString'], [limit, game, days]);
      return selectResult.rows;
    },
  };
};
<|endoftext|>


<commit_before>function roundNumber(num, dec) {
	var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return result;
}

function submitData(con_con, con_sam, test_con, test_sam, fx) {
	var dataString = 'conversions_control='+ con_con + '&samples_control=' + con_sam + '&conversions_experiment='+ test_con + '&samples_experiment=' + test_sam + '&function=' + fx;

	$.getJSON("api?" + dataString, function(stat_results){
		//clear g field
		$(".column").html("");

		//check for errors
		if(stat_results.error){
			return false;
		}

		//add text input with query args, then autofocus it
		var permalinkString = dataString.replace("conversions_control","cc").replace("samples_control","sc").replace("conversions_experiment","ce").replace("samples_experiment","se").replace("function","fx");
		$('<input class="permalink" value="http://'+location.host+location.pathname+'?'+permalinkString+'&permalink=true" type="text">').prependTo("#action").focus();
			$("input[type=text].permalink").focus(function(){
				$(this).select();
			})
			.mouseup(function(e){
				e.preventDefault();
			});

		//insert confidence charts
		var control_confidence = "

"+roundNumber(stat_results.confidence.results.control.low * 100,1)+" - "+roundNumber(stat_results.confidence.results.control.high * 100,1)+"%

Control performance

";
		var experiment_confidence = "

"+roundNumber(stat_results.confidence.results.experiment.low * 100,1)+" - "+roundNumber(stat_results.confidence.results.experiment.high * 100,1)+"%

Test performance

";

		$(control_confidence + experiment_confidence).appendTo(".column.left");

		//insert significance and improvement charts
		var significance = "

"+roundNumber(stat_results.significance.results.experiment * 100,1)+"%

Chance of outperformance

";
		var improvement = "

"+roundNumber(stat_results.improvement.results.low * 100,1)+" - "+roundNumber(stat_results.improvement.results.high * 100,1)+"%

Likely improvement

";

		$(significance + improvement).appendTo(".column.right");
	});

	return false;
}

$(function() {
	$("#con_con").focus();

	$("form").on('submit', function(event){
		event.preventDefault();
		submitData(
			$("input#con_con").val(),
			$("input#con_sam").val(),
			$("input#test_con").val(),
			$("input#test_sam").val(),
			$("input#fx").val()
		);
	});

	//get results on permalink pages
	function getParameter(paramName) {
		var searchString = window.location.search.substring(1),
			i, val, params = searchString.split("&");

		for (i=0;i<params.length;i++) {
			val = params[i].split("=");
			if (val[0] == paramName) {
				return unescape(val[1]);
			}
		}
		return null;
	}

	if(getParameter("permalink") == "true"){
		$("input#con_con").val(getParameter("cc"));
		$("input#con_sam").val(getParameter("sc"));
		$("input#test_con").val(getParameter("ce"));
		$("input#test_sam").val(getParameter("se"));
		submitData(
			getParameter("cc"),
			getParameter("sc"),
			getParameter("ce"),
			getParameter("se"),
			getParameter("fx")
		);
	}
});
<commit_msg>More generic form focus<commit_after>function roundNumber(num, dec) {
	var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return result;
}

function submitData(con_con, con_sam, test_con, test_sam, fx) {
	var dataString = 'conversions_control='+ con_con + '&samples_control=' + con_sam + '&conversions_experiment='+ test_con + '&samples_experiment=' + test_sam + '&function=' + fx;

	$.getJSON("api?" + dataString, function(stat_results){
		//clear g field
		$(".column").html("");

		//check for errors
		if(stat_results.error){
			return false;
		}

		//add text input with query args, then autofocus it
		var permalinkString = dataString.replace("conversions_control","cc").replace("samples_control","sc").replace("conversions_experiment","ce").replace("samples_experiment","se").replace("function","fx");
		$('<input class="permalink" value="http://'+location.host+location.pathname+'?'+permalinkString+'&permalink=true" type="text">').prependTo("#action").focus();
			$("input[type=text].permalink").focus(function(){
				$(this).select();
			})
			.mouseup(function(e){
				e.preventDefault();
			});

		//insert confidence charts
		var control_confidence = "

"+roundNumber(stat_results.confidence.results.control.low * 100,1)+" - "+roundNumber(stat_results.confidence.results.control.high * 100,1)+"%

Control performance

";
		var experiment_confidence = "

"+roundNumber(stat_results.confidence.results.experiment.low * 100,1)+" - "+roundNumber(stat_results.confidence.results.experiment.high * 100,1)+"%

Test performance

";

		$(control_confidence + experiment_confidence).appendTo(".column.left");

		//insert significance and improvement charts
		var significance = "

"+roundNumber(stat_results.significance.results.experiment * 100,1)+"%

Chance of outperformance

";
		var improvement = "

"+roundNumber(stat_results.improvement.results.low * 100,1)+" - "+roundNumber(stat_results.improvement.results.high * 100,1)+"%

Likely improvement

";

		$(significance + improvement).appendTo(".column.right");
	});

	return false;
}

$(function() {
	$("form :input:visible:first").first().focus();

	$("form").on('submit', function(event){
		event.preventDefault();
		submitData(
			$("input#con_con").val(),
			$("input#con_sam").val(),
			$("input#test_con").val(),
			$("input#test_sam").val(),
			$("input#fx").val()
		);
	});

	//get results on permalink pages
	function getParameter(paramName) {
		var searchString = window.location.search.substring(1),
			i, val, params = searchString.split("&");

		for (i=0;i<params.length;i++) {
			val = params[i].split("=");
			if (val[0] == paramName) {
				return unescape(val[1]);
			}
		}
		return null;
	}

	if(getParameter("permalink") == "true"){
		$("input#con_con").val(getParameter("cc"));
		$("input#con_sam").val(getParameter("sc"));
		$("input#test_con").val(getParameter("ce"));
		$("input#test_sam").val(getParameter("se"));
		submitData(
			getParameter("cc"),
			getParameter("sc"),
			getParameter("ce"),
			getParameter("se"),
			getParameter("fx")
		);
	}
});
<|endoftext|>


<commit_before>define(["Tone/core/Tone", "Tone/signal/Signal"], function(Tone){

	/**
	 *  Envelope
	 *  ADR envelope generator attaches to an AudioParam or AudioNode
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {Tone.Time|Object=} attack
	 *  @param {Tone.Time=} decay
	 *  @param {number=} sustain 	a percentage (0-1) of the full amplitude
	 *  @param {Tone.Time=} release
	 */
	Tone.Envelope = function(){

		//get all of the defaults
		var options = this.optionsObject(arguments, ["attack", "decay", "sustain", "release"], Tone.Envelope.defaults);

		/**
		 *  the output
		 *  @type {GainNode}
		 */
		this.output = this.context.createGain();

		/**
		 *  the attack time in seconds
		 *  @type {number}
		 */
		this.attack = this.toSeconds(options.attack);

		/**
		 *  the decay time in seconds
		 *  @type {number}
		 */
		this.decay = this.toSeconds(options.decay);

		/**
		 *  the sustain is a value between 0-1
		 *  @type {number}
		 */
		this.sustain = this.toSeconds(options.sustain);

		/**
		 *  the release time in seconds
		 *  @type {number}
		 */
		this.release = this.toSeconds(options.release);

		/**
		 *  the minimum output of the envelope
		 *  @type {number}
		 */
		this.min = this.toSeconds(options.min);

		/**
		 *  the maximum output of the envelope
		 *  @type {number}
		 */
		this.max = this.toSeconds(options.max);

		/**
		 *  the control signal
		 *  @type {Tone.Signal}
		 *  @private
		 */
		this._control = new Tone.Signal(this.min);

		//connections
		this._control.connect(this.output);
	};

	Tone.extend(Tone.Envelope);

	/**
	 *  the default parameters
	 *
	 *  @static
	 */
	Tone.Envelope.defaults = {
		"attack" : 0.01,
		"decay" : 0.1,
		"sustain" : 0.5,
		"release" : 1,
		"min" : 0,
		"max" : 1
	};

	// SETTERS //

	/**
	 *  set all of the parameters in bulk
	 *  @param {Object} param the name of member as the key
	 *                        and the value as the value
	 */
	Tone.Envelope.prototype.set = function(params){
		if (!this.isUndef(params.attack)) this.setAttack(params.attack);
		if (!this.isUndef(params.decay)) this.setDecay(params.decay);
		if (!this.isUndef(params.sustain)) this.setSustain(params.sustain);
		if (!this.isUndef(params.release)) this.setRelease(params.release);
		if (!this.isUndef(params.min)) this.min = params.min;
		if (!this.isUndef(params.max)) this.max = params.max;
	};

	/**
	 *  set the attack time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setAttack = function(time){
		this.attack = this.toSeconds(time);
	};

	/**
	 *  set the decay time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setDecay = function(time){
		this.decay = this.toSeconds(time);
	};

	/**
	 *  set the release time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setRelease = function(time){
		this.release = this.toSeconds(time);
	};

	/**
	 *  set the sustain amount
	 *  @param {number} sustain value between 0-1
	 */
	Tone.Envelope.prototype.setSustain = function(sustain){
		this.sustain = sustain;
	};

	/**
	 *  set the envelope max
	 *  @param {number} max
	 */
	Tone.Envelope.prototype.setMax = function(max){
		this.max = max;
	};

	/**
	 *  set the envelope min
	 *  @param {number} min
	 */
	Tone.Envelope.prototype.setMin = function(min){
		this.min = min;
	};

	/**
	 * attack->decay->sustain linear ramp
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerAttack = function(time){
		var sustainVal = (this.max - this.min) * this.sustain + this.min;
		if (sustainVal === 0){
			sustainVal = 0.0001;
		}
		if (!time){
			this._control.linearRampToValueNow(this.max, this.attack);
			this._control.exponentialRampToValueAtTime(sustainVal, this.now() + this.attack + this.decay);
		} else {
			var startVal = this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.linearRampToValueAtTime(this.max, time + this.attack);
			this._control.exponentialRampToValueAtTime(sustainVal, time + this.attack + this.decay);
		}
	};

	/**
	 * attack->decay->sustain exponential attack and linear decay
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerExponentialAttack = function(time){
		var sustainVal = (this.max - this.min) * this.sustain + this.min;
		if (!time){
			this._control.exponentialRampToValueNow(this.max, this.attack);
			this._control.linearRampToValueAtTime(sustainVal, this.now() + this.attack + this.decay);
		} else {
			var startVal = this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.exponentialRampToValueAtTime(this.max, time + this.attack);
			this._control.linearRampToValueAtTime(sustainVal, time + this.attack + this.decay);
		}
	};

	/**
	 * triggers the release of the envelope with a linear ramp
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerRelease = function(time){
		if (time){
			//if there's a time, start at the sustain value
			var startVal = (this.max - this.min) * this.sustain + this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.linearRampToValueAtTime(this.min, time + this.toSeconds(this.release));
		} else {
			this._control.linearRampToValueNow(this.min, this.toSeconds(this.release));
		}
	};

	/**
	 * triggers the release of the envelope with an exponential ramp
	 *
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerExponentialRelease = function(time){
		if (time){
			//if there's a time, start at the sustain value
			var startVal = (this.max - this.min) * this.sustain + this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.exponentialRampToValueAtTime(this.min, time + this.toSeconds(this.release));
		} else {
			this._control.exponentialRampToValueNow(this.min, this.toSeconds(this.release));
		}
	};

	/**
	 *  borrows the connect method from {@link Tone.Signal}
	 *
	 *  @function
	 */
	Tone.Envelope.prototype.connect = Tone.Signal.prototype.connect;

	/**
	 *  disconnect and dispose
	 */
	Tone.Envelope.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._control.dispose();
		this._control = null;
	};

	return Tone.Envelope;
});
<commit_msg>min set when min is set<commit_after>define(["Tone/core/Tone", "Tone/signal/Signal"], function(Tone){

	/**
	 *  Envelope
	 *  ADR envelope generator attaches to an AudioParam or AudioNode
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @param {Tone.Time|Object=} attack
	 *  @param {Tone.Time=} decay
	 *  @param {number=} sustain 	a percentage (0-1) of the full amplitude
	 *  @param {Tone.Time=} release
	 */
	Tone.Envelope = function(){

		//get all of the defaults
		var options = this.optionsObject(arguments, ["attack", "decay", "sustain", "release"], Tone.Envelope.defaults);

		/**
		 *  the output
		 *  @type {GainNode}
		 */
		this.output = this.context.createGain();

		/**
		 *  the attack time in seconds
		 *  @type {number}
		 */
		this.attack = this.toSeconds(options.attack);

		/**
		 *  the decay time in seconds
		 *  @type {number}
		 */
		this.decay = this.toSeconds(options.decay);

		/**
		 *  the sustain is a value between 0-1
		 *  @type {number}
		 */
		this.sustain = this.toSeconds(options.sustain);

		/**
		 *  the release time in seconds
		 *  @type {number}
		 */
		this.release = this.toSeconds(options.release);

		/**
		 *  the minimum output of the envelope
		 *  @type {number}
		 */
		this.min = this.toSeconds(options.min);

		/**
		 *  the maximum output of the envelope
		 *  @type {number}
		 */
		this.max = this.toSeconds(options.max);

		/**
		 *  the control signal
		 *  @type {Tone.Signal}
		 *  @private
		 */
		this._control = new Tone.Signal(this.min);

		//connections
		this._control.connect(this.output);
	};

	Tone.extend(Tone.Envelope);

	/**
	 *  the default parameters
	 *
	 *  @static
	 */
	Tone.Envelope.defaults = {
		"attack" : 0.01,
		"decay" : 0.1,
		"sustain" : 0.5,
		"release" : 1,
		"min" : 0,
		"max" : 1
	};

	// SETTERS //

	/**
	 *  set all of the parameters in bulk
	 *  @param {Object} param the name of member as the key
	 *                        and the value as the value
	 */
	Tone.Envelope.prototype.set = function(params){
		if (!this.isUndef(params.attack)) this.setAttack(params.attack);
		if (!this.isUndef(params.decay)) this.setDecay(params.decay);
		if (!this.isUndef(params.sustain)) this.setSustain(params.sustain);
		if (!this.isUndef(params.release)) this.setRelease(params.release);
		if (!this.isUndef(params.min)) this.setMin(params.min);
		if (!this.isUndef(params.max)) this.setMax(params.max);
	};

	/**
	 *  set the attack time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setAttack = function(time){
		this.attack = this.toSeconds(time);
	};

	/**
	 *  set the decay time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setDecay = function(time){
		this.decay = this.toSeconds(time);
	};

	/**
	 *  set the release time
	 *  @param {Tone.Time} time
	 */
	Tone.Envelope.prototype.setRelease = function(time){
		this.release = this.toSeconds(time);
	};

	/**
	 *  set the sustain amount
	 *  @param {number} sustain value between 0-1
	 */
	Tone.Envelope.prototype.setSustain = function(sustain){
		this.sustain = sustain;
	};

	/**
	 *  set the envelope max
	 *  @param {number} max
	 */
	Tone.Envelope.prototype.setMax = function(max){
		this.max = max;
	};

	/**
	 *  set the envelope min
	 *  @param {number} min
	 */
	Tone.Envelope.prototype.setMin = function(min){
		this.min = min;
		//should move the signal to the min
		this._control.setValueAtTime(this.min, this.now());
	};

	/**
	 * attack->decay->sustain linear ramp
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerAttack = function(time){
		var sustainVal = (this.max - this.min) * this.sustain + this.min;
		if (sustainVal === 0){
			sustainVal = 0.0001;
		}
		if (!time){
			this._control.linearRampToValueNow(this.max, this.attack);
			this._control.exponentialRampToValueAtTime(sustainVal, this.now() + this.attack + this.decay);
		} else {
			var startVal = this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.linearRampToValueAtTime(this.max, time + this.attack);
			this._control.exponentialRampToValueAtTime(sustainVal, time + this.attack + this.decay);
		}
	};

	/**
	 * attack->decay->sustain exponential attack and linear decay
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerExponentialAttack = function(time){
		var sustainVal = (this.max - this.min) * this.sustain + this.min;
		if (!time){
			this._control.exponentialRampToValueNow(this.max, this.attack);
			this._control.linearRampToValueAtTime(sustainVal, this.now() + this.attack + this.decay);
		} else {
			var startVal = this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.exponentialRampToValueAtTime(this.max, time + this.attack);
			this._control.linearRampToValueAtTime(sustainVal, time + this.attack + this.decay);
		}
	};

	/**
	 * triggers the release of the envelope with a linear ramp
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerRelease = function(time){
		if (time){
			//if there's a time, start at the sustain value
			var startVal = (this.max - this.min) * this.sustain + this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.linearRampToValueAtTime(this.min, time + this.toSeconds(this.release));
		} else {
			this._control.linearRampToValueNow(this.min, this.toSeconds(this.release));
		}
	};

	/**
	 * triggers the release of the envelope with an exponential ramp
	 *
	 * @param  {Tone.Time=} time
	 */
	Tone.Envelope.prototype.triggerExponentialRelease = function(time){
		if (time){
			//if there's a time, start at the sustain value
			var startVal = (this.max - this.min) * this.sustain + this.min;
			time = this.toSeconds(time);
			this._control.cancelScheduledValues(time);
			this._control.setValueAtTime(startVal, time);
			this._control.exponentialRampToValueAtTime(this.min, time + this.toSeconds(this.release));
		} else {
			this._control.exponentialRampToValueNow(this.min, this.toSeconds(this.release));
		}
	};

	/**
	 *  borrows the connect method from {@link Tone.Signal}
	 *
	 *  @function
	 */
	Tone.Envelope.prototype.connect = Tone.Signal.prototype.connect;

	/**
	 *  disconnect and dispose
	 */
	Tone.Envelope.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._control.dispose();
		this._control = null;
	};

	return Tone.Envelope;
});
<|endoftext|>


<commit_before>var React = require('react'),
	xtend = require('xtend'),
	blacklist = require('blacklist');

var Fixed = React.createClass({

	displayName: 'Fixed',

	propTypes: {
		children: React.PropTypes.node,
		style: React.PropTypes.object
	},

	getInitialState: function() {
		return {
			position: 'relative',
			width: 'auto',
			height: 'auto',
			top: 0
		};
	},

	componentDidMount: function() {

		// Bail in IE8 because React doesn't support the onScroll event in that browser
		// Conveniently (!) IE8 doesn't have window.getComputedStyle which we also use here
		if (!window.getComputedStyle) return;

		var fixed = this.refs.fixed;

		this.windowSize = this.getWindowSize();

		var fixedStyle = window.getComputedStyle(fixed);

		this.fixedSize = {
			x: fixed.offsetWidth,
			y: fixed.offsetHeight + parseInt(fixedStyle.marginTop || '0')
		};

		window.addEventListener('scroll', this.recalcPosition, false);
		window.addEventListener('resize', this.recalcPosition, false);

		this.recalcPosition();
	},

	componentWillUnmount: function() {
		window.removeEventListener('scroll', this.recalcPosition, false);
		window.removeEventListener('resize', this.recalcPosition, false);
	},

	getWindowSize: function() {
		return {
			x: window.innerWidth,
			y: window.innerHeight
		};
	},

	recalcPosition: function() {
		var wrapper = this.refs.wrapper;
		var fixed = this.refs.fixed;

		this.fixedSize.x = wrapper.offsetWidth;

		var offsetTop = 0;
		var offsetEl = wrapper;

		while (offsetEl) {
			offsetTop += offsetEl.offsetTop;
			offsetEl = offsetEl.offsetParent;
		}

		var maxY = offsetTop + this.fixedSize.y;
		var viewY = window.scrollY + window.innerHeight;

		var newSize = this.getWindowSize();
		var sizeChanged = (newSize.x !== this.windowSize.x || newSize.y !== this.windowSize.y);
		this.windowSize = newSize;

		var newState = {
			width: this.fixedSize.x,
			height: this.fixedSize.y
		};

		if (viewY > maxY && (sizeChanged || this.mode !== 'inline')) {
			this.mode = 'inline';
			newState.top = 0;
			newState.position = 'absolute';
			this.setState(newState);
		} else if (viewY <= maxY && (sizeChanged || this.mode !== 'fixed')) {
			this.mode = 'fixed';
			newState.top = window.innerHeight - this.fixedSize.y;
			newState.position = 'fixed';
			this.setState(newState);
		}
	},

	render: function() {
		var wrapperStyle = {
			position: 'relative',
			height: this.state.height
		};
		var fixedProps = blacklist(this.props, 'children', 'style');
		var fixedStyle = xtend(this.props.style || {}, {
			position: this.state.position,
			top: this.state.top,
			width: this.state.width,
			height: this.state.height
		});
		return (

{this.props.children}

		);
	}
});

module.exports = Fixed;
<commit_msg>Cleaning up code style<commit_after>var React = require('react');
var xtend = require('xtend');
var blacklist = require('blacklist');

var Fixed = React.createClass({

	displayName: 'Fixed',

	propTypes: {
		children: React.PropTypes.node,
		style: React.PropTypes.object,
	},

	getInitialState: function() {
		return {
			position: 'relative',
			width: 'auto',
			height: 'auto',
			top: 0,
		};
	},

	componentDidMount: function() {

		// Bail in IE8 because React doesn't support the onScroll event in that browser
		// Conveniently (!) IE8 doesn't have window.getComputedStyle which we also use here
		if (!window.getComputedStyle) return;

		var fixed = this.refs.fixed;

		this.windowSize = this.getWindowSize();

		var fixedStyle = window.getComputedStyle(fixed);

		this.fixedSize = {
			x: fixed.offsetWidth,
			y: fixed.offsetHeight + parseInt(fixedStyle.marginTop || '0'),
		};

		window.addEventListener('scroll', this.recalcPosition, false);
		window.addEventListener('resize', this.recalcPosition, false);

		this.recalcPosition();
	},

	componentWillUnmount: function() {
		window.removeEventListener('scroll', this.recalcPosition, false);
		window.removeEventListener('resize', this.recalcPosition, false);
	},

	getWindowSize: function() {
		return {
			x: window.innerWidth,
			y: window.innerHeight
		};
	},

	recalcPosition: function() {
		var wrapper = this.refs.wrapper;
		var fixed = this.refs.fixed;

		this.fixedSize.x = wrapper.offsetWidth;

		var offsetTop = 0;
		var offsetEl = wrapper;

		while (offsetEl) {
			offsetTop += offsetEl.offsetTop;
			offsetEl = offsetEl.offsetParent;
		}

		var maxY = offsetTop + this.fixedSize.y;
		var viewY = window.scrollY + window.innerHeight;

		var newSize = this.getWindowSize();
		var sizeChanged = (newSize.x !== this.windowSize.x || newSize.y !== this.windowSize.y);
		this.windowSize = newSize;

		var newState = {
			width: this.fixedSize.x,
			height: this.fixedSize.y,
		};

		if (viewY > maxY && (sizeChanged || this.mode !== 'inline')) {
			this.mode = 'inline';
			newState.top = 0;
			newState.position = 'absolute';
			this.setState(newState);
		} else if (viewY <= maxY && (sizeChanged || this.mode !== 'fixed')) {
			this.mode = 'fixed';
			newState.top = window.innerHeight - this.fixedSize.y;
			newState.position = 'fixed';
			this.setState(newState);
		}
	},

	render: function() {
		var wrapperStyle = {
			position: 'relative',
			height: this.state.height,
		};
		var fixedProps = blacklist(this.props, 'children', 'style');
		var fixedStyle = xtend(this.props.style || {}, {
			position: this.state.position,
			top: this.state.top,
			width: this.state.width,
			height: this.state.height,
		});
		return (

{this.props.children}

		);
	}
});

module.exports = Fixed;
<|endoftext|>


<commit_before>var express   = require('express')
    , http    = require('http')
    , async   = require('async')
    , multer  = require('multer')
    , upload  = multer({ dest: 'uploads/' })
    , exphbs  = require('express-handlebars')
    , easyimg = require('easyimage')
    , _  = require('lodash')
    , cv      = require('opencv');

// MIME types for image uploads
var exts = {
  'image/jpeg': '.jpg',
  'image/png' : '.png',
  'image/gif' : '.gif'
};

var port = 8080;
var app = express();
app.use(express.static(__dirname + '/public'))

// Configure Handlebars
app.engine('.hbs', exphbs({ extname: '.hbs', defaultLayout: 'default' }));
app.set('view engine', '.hbs');

/**
 * This is a placeholder for the application code
 */

app.get('/', function( req, res, next ) {
  return res.render('index');
});

/**
 * POST callback for the file upload form. This is where the magic happens.
 */
app.post('/upload', upload.single('file'), function(req, res, next){

  // Generate a filename; just use the one generated for us, plus the appropriate extension
  var filename = req.file.filename + exts[req.file.mimetype]
    // and source and destination filepaths
    , src = __dirname + '/' + req.file.path
    , dst = __dirname + '/public/images/' + filename;
  /**
   * Go through the various steps
   */
  async.waterfall(
    [
      function( callback ) {
        /**
         * Check the mimetype to ensure the uploaded file is an image
         */
        if (!_.includes(
          [
            'image/jpeg',
            'image/png',
            'image/gif'
          ],
          req.file.mimetype
        ) ) {
          return callback( new Error( 'Invalid file - please upload an image (.jpg, .png, .gif).' ) )
        }

        return callback();
      },
      function( callback ) {
        /**
         * Get some information about the uploaded file
         */
        easyimg.info( src ).then(
          function(file) {
            /**
             * Check that the image is suitably large
             */
            if ( ( file.width < 960 ) || ( file.height < 300 ) ) {
              return callback( new Error( 'Image must be at least 640 x 300 pixels' ) );
            }
            return callback();
          }
        );
      },
      function( callback ) {

        /**
         * Resize the image to a sensible size
         */
        easyimg.resize(
          {
            width      :   960,
            src        :   src,
            dst        :   dst
          }
        ).then(function(image) {
          return callback();
        });
      },
      function( callback ) {
        /**
         * Use OpenCV to read the (resized) image
         */
        cv.readImage( dst, callback );
      },
      function( im, callback ) {
        /**
         * Run the face detection algorithm
         */
        im.detectObject( cv.FACE_CASCADE, {}, callback );
      }
    ],
    function( err, faces ) {
      /**
       * If an error occurred somewhere along the way, render the
       * error page.
       */
      if ( err ) {

        return res.render(
          'error',
          {
            message : err.message
          }
        );
      }

      /**
       * We're all good; render the result page.
       */
      return res.render(
        'result',
        {
          filename   :   filename,
          faces     :   faces
        }
      );
    }
  );
});

http.createServer(app)
  .listen(port, function(server) {
    console.log('Face detection with OpenCV. Listening on port http://localhost:%d', port);
  });<commit_msg>quitar next<commit_after>var express   = require('express')
    , http    = require('http')
    , async   = require('async')
    , multer  = require('multer')
    , upload  = multer({ dest: 'uploads/' })
    , exphbs  = require('express-handlebars')
    , easyimg = require('easyimage')
    , _  = require('lodash')
    , cv      = require('opencv');

// MIME types for image uploads
var exts = {
  'image/jpeg': '.jpg',
  'image/png' : '.png',
  'image/gif' : '.gif'
};

var port = 8080;
var app = express();
app.use(express.static(__dirname + '/public'));

// Configure Handlebars
app.engine('.hbs', exphbs({ extname: '.hbs', defaultLayout: 'default' }));
app.set('view engine', '.hbs');

/**
 * This is a placeholder for the application code
 */

app.get('/', function( req, res ) {
  return res.render('index');
});

/**
 * POST callback for the file upload form. This is where the magic happens.
 */
app.post('/upload', upload.single('file'), function(req, res){

  // Generate a filename; just use the one generated for us, plus the appropriate extension
  var filename = req.file.filename + exts[req.file.mimetype]
    // and source and destination filepaths
    , src = __dirname + '/' + req.file.path
    , dst = __dirname + '/public/images/' + filename;
  /**
   * Go through the various steps
   */
  async.waterfall(
    [
      function( callback ) {
        /**
         * Check the mimetype to ensure the uploaded file is an image
         */
        if (!_.includes(
          [
            'image/jpeg',
            'image/png',
            'image/gif'
          ],
          req.file.mimetype
        ) ) {
          return callback( new Error( 'Invalid file - please upload an image (.jpg, .png, .gif).' ) )
        }

        return callback();
      },
      function( callback ) {
        /**
         * Get some information about the uploaded file
         */
        easyimg.info( src ).then(
          function(file) {
            /**
             * Check that the image is suitably large
             */
            if ( ( file.width < 960 ) || ( file.height < 300 ) ) {
              return callback( new Error( 'Image must be at least 640 x 300 pixels' ) );
            }
            return callback();
          }
        );
      },
      function( callback ) {

        /**
         * Resize the image to a sensible size
         */
        easyimg.resize(
          {
            width      :   960,
            src        :   src,
            dst        :   dst
          }
        ).then(function(image) {
          return callback();
        });
      },
      function( callback ) {
        /**
         * Use OpenCV to read the (resized) image
         */
        cv.readImage( dst, callback );
      },
      function( im, callback ) {
        /**
         * Run the face detection algorithm
         */
        im.detectObject( cv.FACE_CASCADE, {}, callback );
      }
    ],
    function( err, faces ) {
      /**
       * If an error occurred somewhere along the way, render the
       * error page.
       */
      if ( err ) {

        return res.render(
          'error',
          {
            message : err.message
          }
        );
      }

      /**
       * We're all good; render the result page.
       */
      return res.render(
        'result',
        {
          filename   :   filename,
          faces     :   faces
        }
      );
    }
  );
});

http.createServer(app)
  .listen(port, function(server) {
    console.log('Face detection with OpenCV. Listening on port http://localhost:%d', port);
  });<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import * as actions from '../actions/actions';

import Conversation from '../components/conversation';

class ConversationContainer extends Component {
    constructor(props) {
        super(props);
        this.id = -1;
    }

    componentWillMount() {
        const {dispatch, user, conversation, params, socket } = this.props;
        let self = this;
        socket.on('new socket server', function (msg) {
            if(msg.conversationId == params.conversationId) {
                dispatch(actions.getSocketMessage(msg.message));
                dispatch(actions.fetchConversations(user._id));
                self.forceUpdate();
            }
        });
    }

    componentDidUpdate() {
        if (this.id != this.props.params.conversationId) {
            this.props.dispatch(actions.fetchMessages(this.props.params.conversationId));
            this.id = this.props.params.conversationId;
        }
        const messagesList = this.refs.messageList;
        messagesList.scrollTop = messagesList.scrollHeight;
    }

    render() {
        const {dispatch, user, conversation} = this.props;
        return (

                {
                    conversation.loaded &&
                    <Conversation conversation={conversation} user={user} dispatch={dispatch}/>
                }

        )
    }
}

export default ConversationContainer;
<commit_msg>minor change made the difference :D<commit_after>import React, {Component, PropTypes} from 'react';
import * as actions from '../actions/actions';

import Conversation from '../components/conversation';

class ConversationContainer extends Component {
    constructor(props) {
        super(props);
        this.id = -1;
    }

    componentWillMount() {
        const {dispatch, user, conversation, params, socket } = this.props;
        let self = this;
        socket.on('new socket server', function (msg) {
            console.log(msg.conversationId);
            console.log(params.conversationId);
            if(msg.conversationId == this.id) {
                dispatch(actions.getSocketMessage(msg.message));
                dispatch(actions.fetchConversations(user._id));
                self.forceUpdate();
            }
        });
    }

    componentDidUpdate() {
        console.log('component did update');
        console.log(this.id);
        console.log(this.props.params.conversationId);
        if (this.id != this.props.params.conversationId) {
            console.log('updating conversation messages composer');
            this.props.dispatch(actions.fetchMessages(this.props.params.conversationId));
            this.id = this.props.params.conversationId;
        }
        const messagesList = this.refs.messageList;
        messagesList.scrollTop = messagesList.scrollHeight;
    }

    render() {
        const {dispatch, user, conversation} = this.props;
        return (

                {
                    conversation.loaded &&
                    <Conversation conversation={conversation} user={user} dispatch={dispatch}/>
                }

        )
    }
}

export default ConversationContainer;
<|endoftext|>


<commit_before>import puppeteer from 'puppeteer';
import path from 'path';

import EssentialHttpServer from '../../src/essential-http-server';

const port = 3001;
const server = new EssentialHttpServer({
    rootDir: path.resolve(__dirname, '../../public/'),
    port,
});
const sitePath = `http://localhost:${port}`;

server.start(async () => {
    const browser = await puppeteer.launch({
        headless: false
    });
    const page = await browser.newPage();

    await page.goto(sitePath);
    await page.click('a');
    await page.goto(`${sitePath}/no-page.html`);

    browser.close();
});
<commit_msg>prettier<commit_after>import puppeteer from 'puppeteer';
import path from 'path';

import EssentialHttpServer from '../../src/essential-http-server';

const port = 3001;
const server = new EssentialHttpServer({
  rootDir: path.resolve(__dirname, '../../public/'),
  port,
});
const sitePath = `http://localhost:${port}`;

server.start(async () => {
  const browser = await puppeteer.launch({
    headless: false,
  });
  const page = await browser.newPage();

  await page.goto(sitePath);
  await page.click('a');
  await page.goto(`${sitePath}/no-page.html`);

  browser.close();
});
<|endoftext|>


<commit_before>$(function() {
	/* variables */
	var t;

	$("#tabs_infos").tabs();

	/* $("#accordion").accordion({
		collapsible : true,
		active : false,
		clearStyle:true,
		autoHeight : false
	});*/

	/*$('#accordion').bind('accordionchange', function(event,ui) {
		$('#accordion h3.ui-state-focus').removeClass('.ui-state-focus');
	});*/

	$("#slider").easySlider({
		auto: true,
		continuous: true,
		pause : 4000
	});

	// run the currently selected effect
	function runEffect() {

		// most effect types need no options passed by default
		var options = {};

		// run the effect
		//$("#ul_vous-etes").stop();
		//$( "#ul_vous-etes" ).show( "blind", options, 2000 );
		clearTimeout(t);
		$("#ul_vous-etes").show();

	};

	function stopEffect() {

		// most effect types need no options passed by default
		var options = {};

		// stop the effect
		//$( "#ul_vous-etes" ).hide( "blind", options, 2000 );
                t = setTimeout('$( "#ul_vous-etes" ).hide();', 1000);
	};

	// set effect from select menu value
	$( "#nav_vous-etes" ).hover(function() {
			runEffect();
			return false;
		},
                function() {
       			stopEffect();
			return false;
		}
	);
});
window.onload = function () {

        var ul = $('div.sliderGallery ul');
        var itemsWidth = 1000;

        $('.slider').slider({
            min: 0,
            max: itemsWidth,
            handle: '.handle',
            stop: function (event, ui) {
                ul.animate({'left' : ui.value * -1}, 500);
            },
            slide: function (event, ui) {
                ul.css('left', ui.value * -1);
            }
        });

 $(".items img").click(function() {
		// see if same thumb is being clicked
		if ($(this).hasClass("active")) { return; }

		// calclulate large image's URL based on the thumbnail URL
		var url = $(this).attr("src").replace("/thumb", "/medium");

		// get handle to element that wraps the image and make it semi-transparent
		var wrap = $("#big_image") //.fadeTo("medium", 0.5);

		// the large image
		var img = new Image();

		// call this function after it's loaded
		img.onload = function() {

			// make wrapper fully visible
			//wrap.fadeTo("fast", 1);

			// change the image
			wrap.find("img").attr("src", url);

		};

		// begin loading the image from www.flickr.com
		img.src = url;

		var dlink = "/press_downloads/" + $(this).attr("alt") + "/download_visual";
			$("#show_visual .download_button").find("a").attr("href",dlink);

		// activate item
		$(".items img").removeClass("active");
		$(this).addClass("active");
// when page loads simulate a "click" on the first image
}).filter(":first").click();

    };
<commit_msg>modif css<commit_after>$(function() {
	/* variables */
	var t;

	$("#tabs_infos").tabs();
        if( $("#slider > ul > li").size() < 2){
		$("#slider").easySlider({
			auto: false,
			continuous: false,
			controlsShow : false,
			pause : 4000
		});
	}
	else{
		$("#slider").easySlider({
			auto: true,
			continuous: true,
			pause : 4000
		});
	}

	// run the currently selected effect
	function runEffect() {

		// most effect types need no options passed by default
		var options = {};

		// run the effect
		clearTimeout(t);
		$("#ul_vous-etes").show();

	};

	function stopEffect() {

		// most effect types need no options passed by default
		var options = {};

		// stop the effect
                t = setTimeout('$( "#ul_vous-etes" ).hide();', 1000);
	};

	// set effect from select menu value
	$( "#nav_vous-etes" ).hover(function() {
			runEffect();
			return false;
		},
                function() {
       			stopEffect();
			return false;
		}
	);
});
window.onload = function () {

        var ul = $('div.sliderGallery ul');
        var itemsWidth = 1000;

        $('.slider').slider({
            min: 0,
            max: itemsWidth,
            handle: '.handle',
            stop: function (event, ui) {
                ul.animate({'left' : ui.value * -1}, 500);
            },
            slide: function (event, ui) {
                ul.css('left', ui.value * -1);
            }
        });

 $(".items img").click(function() {
		// see if same thumb is being clicked
		if ($(this).hasClass("active")) { return; }

		// calclulate large image's URL based on the thumbnail URL
		var url = $(this).attr("src").replace("/thumb", "/medium");

		// get handle to element that wraps the image and make it semi-transparent
		var wrap = $("#big_image") //.fadeTo("medium", 0.5);

		// the large image
		var img = new Image();

		// call this function after it's loaded
		img.onload = function() {

			// make wrapper fully visible
			//wrap.fadeTo("fast", 1);

			// change the image
			wrap.find("img").attr("src", url);

		};

		// begin loading the image from www.flickr.com
		img.src = url;

		var dlink = "/press_downloads/" + $(this).attr("alt") + "/download_visual";
			$("#show_visual .download_button").find("a").attr("href",dlink);

		// activate item
		$(".items img").removeClass("active");
		$(this).addClass("active");
// when page loads simulate a "click" on the first image
}).filter(":first").click();

    };
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractView = require("modules/view/AbstractView");
    var FileAPIUtil = require("modules/util/FileAPIUtil");

    /**
     * 記事一覧アイテム(メニュー用)のViewを作成する。
     *
     * @class 記事一覧アイテム(メニュー用)のView
     * @exports GridListItemView
     * @constructor
     */
    var GridListItemView = AbstractView.extend({
        /**
         * このViewのテンプレートファイパス
         */
        template : require("ldsh!templates/{mode}/news/gridListItem"),

        /**
         * ViewのテンプレートHTMLの描画処理が完了した後に呼び出される。
         * <p>
         * 記事に関連する画像ファイルの取得と表示を行う。
         * </p>
         */
        afterRendered : function() {
            var self = this;
            var $articleImage = this.$el.find(".articleImage");

            if (this.model.get("imageUrl")) {
                $articleImage.attr("src", this.model.get("imageUrl"));
            } else if (this.model.get("fileName")) {
                // TODO サムネイル画像取得処理に変更するべき
                var onGetBinary = function(binary) {
                    var arrayBufferView = new Uint8Array(binary);
                    var blob = new Blob([
                        arrayBufferView
                    ], {
                        type : "image/jpg"
                    });
                    var url = FileAPIUtil.createObjectURL(blob);

                    $articleImage
                        .on("load", function() {
                            $(this).parent().show();
                            window.URL.revokeObjectURL($(this).attr("src"));
                        })
                        .attr("src", url);
                };

                try {
                    app.box
                        .col("dav")
                        .getBinary(this.model.get("fileName"), {
                            success : onGetBinary
                        });
                } catch (e) {
                    console.error(e);
                }

                $articleImage.parent().hide();
            } else {
                $articleImage.parent().hide();
            }
        }
    });

    module.exports = GridListItemView;
});
<commit_msg>NAM-396 新聞アプリの一覧表示のレイアウト変更<commit_after>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var FeedListItemView = require("modules/view/news/FeedListItemView");

    /**
     * 記事一覧アイテム(メニュー用)のViewを作成する。
     *
     * @class 記事一覧アイテム(メニュー用)のView
     * @exports GridListItemView
     * @constructor
     */
    var GridListItemView = FeedListItemView.extend({
        /**
         * このViewのテンプレートファイパス
         */
        template : require("ldsh!templates/{mode}/news/gridListItem"),

    });

    module.exports = GridListItemView;
});
<|endoftext|>