<commit_before>// NOTE: this is one of the places where I'm using internal state rather than redux global state
// reason for doing so is because this intended to be used multiple times in a giant listview
// it's therefore easier to keep the state attached to the component rather than build a giant array in redux

import React, {Component} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    TouchableHighlight,
    TextInput,
    Keyboard,
    Platform
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import Pill from 'app/shared_features/pill/Pill';

class SetForm extends Component {

    constructor(props) {
        super(props);

        this.state = {
            setNumber: this.props.setNumber,
            exercise: this.props.exercise,
            tags: this.props.tags,
            weight: this.props.weight,
            didChangeWeight: false,
            metric: this.props.metric,
            rpe: this.props.rpe,
            didChangeRPE: false,
            removed: this.props.removed,
            editingExercise: false,
            suggestions: []
        };
    }

    // this uses didChange flags to prevent overwriting of values while you're still typing while still allowing sync to update it
    componentWillReceiveProps(nextProps) {
        this.setState({
            setNumber: nextProps.setNumber,
            exercise: nextProps.exercise,
            tags: nextProps.tags,
            weight: this.state.didChangeWeight ? this.state.weight : nextProps.weight,
            didChangeWeight: false,
            metric: nextProps.metric,
            rpe: this.state.didChangeRPE ? this.state.rpe : nextProps.rpe,
            didChangeRPE: false,
            removed: nextProps.removed
        });
    }

    // KEYBOARD

    componentWillMount() {
        this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { this._keyboardDidHide() });
    }

    componentWillUnmount() {
        this.keyboardDidHideListener.remove();
    }

    _keyboardDidHide() {
        this._save();
    }

    _onChangeWeight(weight) {
        this.setState({
            weight: weight,
            didChangeWeight: true
        });
        this.props.saveSet(this.props.setID, this.state.exercise, this.state.weight, this.state.metric, this.state.rpe);
    }

    _onChangeRPE(rpe) {
        this.setState({
            rpe: rpe,
            didChangeRPE: true
        });
        this.props.saveSet(this.props.setID, this.state.exercise, this.state.weight, this.state.metric, this.state.rpe);
    }

    // SAVING

    componentDidUpdate(nextProps, nextState) {
        if (nextState.metric !== this.state.metric) {
            this._save();
        }
    }

    _save() {
        if (this.props.exercise !== this.state.exercise
            || this.props.weight !== this.state.weight
            || this.props.metric !== this.state.metric
            || this.props.rpe !== this.state.rpe) {
            this.props.saveSet(this.props.setID, this.state.exercise, this.state.weight, this.state.metric, this.state.rpe);
        }
    }

    _toggleMetric() {
        if (this.state.metric === 'kgs') {
            this.setState({metric: 'lbs'})
        } else if (this.state.metric === 'lbs') {
            this.setState({metric: 'kgs'})
        }
    }

    // RENDER

    _displayExercise() {
        if (this.state.exercise === null || this.state.exercise === '') {
            return (<Text style={[styles.exerciseText, styles.placeholderText]}>Enter Exercise</Text>);
        }
        return (<Text style={styles.exerciseText}>{this.state.exercise}</Text>);
    }

    _displayTags() {
        if (this.state.tags === undefined || this.state.tags === null || this.state.tags.length === 0) {
            return (<Text style={[styles.exerciseText, styles.placeholderText]}>Enter Tags</Text>);
        }

        var pills = [];
        let position = 0;
        this.state.tags.map((tag) => {
            let key = position;
            pills.push(
                <Pill key={key} text={tag} style={{paddingRight: 5, paddingBottom: 3}} />
            );
            position++;
        });

        return (<View style={styles.tagField}>{pills}</View>);
    }

    _displaySetNumber() {
        if (this.state.removed) {
            return null;
        }
        if (this.state.setNumber === null || this.state.setNumber === undefined) {
            return '#1';
        }
        return '#' + this.state.setNumber;
    }

    _displayMetric() {
        if (this.state.metric === 'kgs') {
            return "KGs";
        } else if (this.state.metric === 'lbs') {
            return "LBs";
        }
    }

    _renderExercise() {
        return (
            <View style={styles.field}>
                <TouchableHighlight onPress={() => this.props.tapExercise(this.props.setID, this.state.exercise, this.props.bias)} underlayColor='lightgray'>
                    {this._displayExercise()}
                </TouchableHighlight>
                <View style={styles.fieldDetails} pointerEvents='none'>
                    <Text style={styles.detailText}>{this._displaySetNumber()}</Text>
                </View>
            </View>
        );
    }

    _renderWeight() {
        return (
            <View style={[styles.field, {flex: 1, marginRight: 5}]}>
                <TextInput
                    style={styles.fieldText}
                    keyboardType={'numeric'}
                    underlineColorAndroid={'transparent'}
                    editable = {true}
                    placeholder="Enter"
                    placeholderTextColor={'lightgray'}
                    value={this.state.weight}

                    onEndEditing={() => this.props.dismissWeight()}
                    onFocus={() => this.props.tapWeight()}
                    onChangeText={(weight) => this._onChangeWeight(weight) }
                />
                <View style={styles.fieldDetails}>
                    <TouchableOpacity onPress={() => this._toggleMetric()}>
                        <View style={{flexDirection: 'row', alignItems: 'center', }}>
                            <Text style={styles.detailText}>{this._displayMetric()} </Text>
                            <Icon name="refresh" size={10} color="gray" />
                        </View>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }

    _renderRPE() {
        return (
            <View style={[styles.field, {flex: 1}]}>
                <TextInput
                    style={styles.fieldText}
                    keyboardType={'numeric'}
                    underlineColorAndroid={'transparent'}
                    editable = {true}
                    placeholder="Enter"
                    onEndEditing={() => this.props.dismissRPE()}
                    placeholderTextColor={'lightgray'}
                    value = {this.state.rpe}
                    onFocus={() => this.props.tapRPE() }
                    onChangeText={(rpe) => this._onChangeRPE(rpe) }
                />
                <View style={styles.fieldDetails} pointerEvents='none'>
                    <Text style={styles.detailText}>RPE</Text>
                </View>
            </View>
        );
    }

    _renderTags() {
        return (
            <View style={[styles.field, {flex: 1}]}>
                <TouchableHighlight onPress={() => this.props.tapTags(this.props.setID, this.state.tags)} underlayColor='lightgray'>
                    {this._displayTags()}
                </TouchableHighlight>
            </View>
        );
    }

    render() {
        return (
            <View style={{flex: 1, flexDirection: 'column'}}>
                <View style={styles.upperShadow} />
                <View style={[styles.shadow, {flex: 1, flexDirection: 'column', padding: 5}]}>
                    <View style={{flex: 1, flexDirection: 'row', justifyContent: 'space-between'}}>
                        <View style={{flex: 1}}>
                            { this._renderExercise() }
                            <View style={{flex: 1, flexDirection: 'row'}}>
                                { this._renderWeight() }
                                { this._renderRPE() }
                            </View>
                        </View>

                        <View>{ this.props.renderDetailComponent() }</View>
                    </View>

                    <View>{ this._renderTags() }</View>
                </View>
            </View>
        );
    }

}

const styles = StyleSheet.create({
    field: {
        backgroundColor: 'rgba(239, 239, 239, 1)',
        borderColor: 'rgba(239, 239, 239, 1)',
        borderWidth: 3,
        borderRadius: 3,
        marginBottom: 5,
        zIndex: 2,
        minHeight: (Platform.OS === 'ios') ? 30 : 40,
    },
    fieldDetails: {
        position: 'absolute',
        right: 5,
        top: 0,
        bottom: 0,
        justifyContent: 'center',
    },
    fieldText: {
        height: (Platform.OS === 'ios') ? 30 : 40,
        fontSize: 15,
        paddingRight: 30
    },
    exerciseText: {
        height: (Platform.OS === 'ios') ? 30 : 40,
        paddingTop: (Platform.OS === 'ios') ? 6 : 8,
        paddingLeft: (Platform.OS === 'ios') ? 0 : 4,
        fontSize: 15,
        paddingRight: 30,
        color: 'black'
    },
    tagField: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        minHeight: (Platform.OS === 'ios') ? 30 : 40,
        paddingLeft: 2,
        paddingRight: 0,
    },
    placeholderText: {
        color: 'lightgray'
    },
    detailText: {
        color: 'gray',
        backgroundColor: 'rgba(0, 0, 0, 0)'
    },
    upperShadow: {
        shadowColor: "#000000",
        shadowRadius: 2,
        shadowOpacity: 1,
        shadowOffset: {
            height: 1,
            weight: 0
        },
        height: 1,
        backgroundColor: 'white'
    },
    shadow: {
        shadowColor: "#000000",
        shadowOpacity: 0.2,
        shadowRadius: 2,
        shadowOffset: {
            height: 4,
            width: 0
        },
    }
});

export default SetForm;
<commit_msg>Saving values on ending weight or rpe<commit_after>// NOTE: this is one of the places where I'm using internal state rather than redux global state
// reason for doing so is because this intended to be used multiple times in a giant listview
// it's therefore easier to keep the state attached to the component rather than build a giant array in redux

import React, {Component} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    TouchableHighlight,
    TextInput,
    Keyboard,
    Platform
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import Pill from 'app/shared_features/pill/Pill';

class SetForm extends Component {

    constructor(props) {
        super(props);

        this.state = {
            setNumber: this.props.setNumber,
            exercise: this.props.exercise,
            tags: this.props.tags,
            weight: this.props.weight,
            didChangeWeight: false,
            metric: this.props.metric,
            rpe: this.props.rpe,
            didChangeRPE: false,
            removed: this.props.removed,
            editingExercise: false,
            suggestions: []
        };
    }

    // this uses didChange flags to prevent overwriting of values while you're still typing while still allowing sync to update it
    componentWillReceiveProps(nextProps) {
        this.setState({
            setNumber: nextProps.setNumber,
            exercise: nextProps.exercise,
            tags: nextProps.tags,
            weight: this.state.didChangeWeight ? this.state.weight : nextProps.weight,
            didChangeWeight: false,
            metric: nextProps.metric,
            rpe: this.state.didChangeRPE ? this.state.rpe : nextProps.rpe,
            didChangeRPE: false,
            removed: nextProps.removed
        });
    }

    // KEYBOARD

    componentWillMount() {
        this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { this._keyboardDidHide() });
    }

    componentWillUnmount() {
        this.keyboardDidHideListener.remove();
    }

    _keyboardDidHide() {
        this._save();
    }

    _onChangeWeight(weight) {
        this.setState({
            weight: weight,
            didChangeWeight: true
        });
    }

    _onChangeRPE(rpe) {
        this.setState({
            rpe: rpe,
            didChangeRPE: true
        });
    }

    _onEndEditWeight() {
        this._save();
        this.props.dismissWeight();
    }

    _onEndEditRPE() {
        this._save();
        this.props.dismissRPE();
    }

    // SAVING

    componentDidUpdate(nextProps, nextState) {
        if (nextState.metric !== this.state.metric) {
            this._save();
        }
    }

    _save() {
        if (this.props.exercise !== this.state.exercise
            || this.props.weight !== this.state.weight
            || this.props.metric !== this.state.metric
            || this.props.rpe !== this.state.rpe) {
            this.props.saveSet(this.props.setID, this.state.exercise, this.state.weight, this.state.metric, this.state.rpe);
        }
    }

    _toggleMetric() {
        if (this.state.metric === 'kgs') {
            this.setState({metric: 'lbs'})
        } else if (this.state.metric === 'lbs') {
            this.setState({metric: 'kgs'})
        }
    }

    // RENDER

    _displayExercise() {
        if (this.state.exercise === null || this.state.exercise === '') {
            return (<Text style={[styles.exerciseText, styles.placeholderText]}>Enter Exercise</Text>);
        }
        return (<Text style={styles.exerciseText}>{this.state.exercise}</Text>);
    }

    _displayTags() {
        if (this.state.tags === undefined || this.state.tags === null || this.state.tags.length === 0) {
            return (<Text style={[styles.exerciseText, styles.placeholderText]}>Enter Tags</Text>);
        }

        var pills = [];
        let position = 0;
        this.state.tags.map((tag) => {
            let key = position;
            pills.push(
                <Pill key={key} text={tag} style={{paddingRight: 5, paddingBottom: 3}} />
            );
            position++;
        });

        return (<View style={styles.tagField}>{pills}</View>);
    }

    _displaySetNumber() {
        if (this.state.removed) {
            return null;
        }
        if (this.state.setNumber === null || this.state.setNumber === undefined) {
            return '#1';
        }
        return '#' + this.state.setNumber;
    }

    _displayMetric() {
        if (this.state.metric === 'kgs') {
            return "KGs";
        } else if (this.state.metric === 'lbs') {
            return "LBs";
        }
    }

    _renderExercise() {
        return (
            <View style={styles.field}>
                <TouchableHighlight onPress={() => this.props.tapExercise(this.props.setID, this.state.exercise, this.props.bias)} underlayColor='lightgray'>
                    {this._displayExercise()}
                </TouchableHighlight>
                <View style={styles.fieldDetails} pointerEvents='none'>
                    <Text style={styles.detailText}>{this._displaySetNumber()}</Text>
                </View>
            </View>
        );
    }

    _renderWeight() {
        return (
            <View style={[styles.field, {flex: 1, marginRight: 5}]}>
                <TextInput
                    style={styles.fieldText}
                    keyboardType={'numeric'}
                    underlineColorAndroid={'transparent'}
                    editable = {true}
                    placeholder="Enter"
                    placeholderTextColor={'lightgray'}
                    value={this.state.weight}

                    onEndEditing={() => this._onEndEditWeight() }
                    onFocus={() => this.props.tapWeight()}
                    onChangeText={(weight) => this._onChangeWeight(weight) }
                />
                <View style={styles.fieldDetails}>
                    <TouchableOpacity onPress={() => this._toggleMetric()}>
                        <View style={{flexDirection: 'row', alignItems: 'center', }}>
                            <Text style={styles.detailText}>{this._displayMetric()} </Text>
                            <Icon name="refresh" size={10} color="gray" />
                        </View>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }

    _renderRPE() {
        return (
            <View style={[styles.field, {flex: 1}]}>
                <TextInput
                    style={styles.fieldText}
                    keyboardType={'numeric'}
                    underlineColorAndroid={'transparent'}
                    editable = {true}
                    placeholder="Enter"
                    onEndEditing={() => this._onEndEditRPE() }
                    placeholderTextColor={'lightgray'}
                    value = {this.state.rpe}
                    onFocus={() => this.props.tapRPE() }
                    onChangeText={(rpe) => this._onChangeRPE(rpe) }
                />
                <View style={styles.fieldDetails} pointerEvents='none'>
                    <Text style={styles.detailText}>RPE</Text>
                </View>
            </View>
        );
    }

    _renderTags() {
        return (
            <View style={[styles.field, {flex: 1}]}>
                <TouchableHighlight onPress={() => this.props.tapTags(this.props.setID, this.state.tags)} underlayColor='lightgray'>
                    {this._displayTags()}
                </TouchableHighlight>
            </View>
        );
    }

    render() {
        return (
            <View style={{flex: 1, flexDirection: 'column'}}>
                <View style={styles.upperShadow} />
                <View style={[styles.shadow, {flex: 1, flexDirection: 'column', padding: 5}]}>
                    <View style={{flex: 1, flexDirection: 'row', justifyContent: 'space-between'}}>
                        <View style={{flex: 1}}>
                            { this._renderExercise() }
                            <View style={{flex: 1, flexDirection: 'row'}}>
                                { this._renderWeight() }
                                { this._renderRPE() }
                            </View>
                        </View>

                        <View>{ this.props.renderDetailComponent() }</View>
                    </View>

                    <View>{ this._renderTags() }</View>
                </View>
            </View>
        );
    }

}

const styles = StyleSheet.create({
    field: {
        backgroundColor: 'rgba(239, 239, 239, 1)',
        borderColor: 'rgba(239, 239, 239, 1)',
        borderWidth: 3,
        borderRadius: 3,
        marginBottom: 5,
        zIndex: 2,
        minHeight: (Platform.OS === 'ios') ? 30 : 40,
    },
    fieldDetails: {
        position: 'absolute',
        right: 5,
        top: 0,
        bottom: 0,
        justifyContent: 'center',
    },
    fieldText: {
        height: (Platform.OS === 'ios') ? 30 : 40,
        fontSize: 15,
        paddingRight: 30
    },
    exerciseText: {
        height: (Platform.OS === 'ios') ? 30 : 40,
        paddingTop: (Platform.OS === 'ios') ? 6 : 8,
        paddingLeft: (Platform.OS === 'ios') ? 0 : 4,
        fontSize: 15,
        paddingRight: 30,
        color: 'black'
    },
    tagField: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        minHeight: (Platform.OS === 'ios') ? 30 : 40,
        paddingLeft: 2,
        paddingRight: 0,
    },
    placeholderText: {
        color: 'lightgray'
    },
    detailText: {
        color: 'gray',
        backgroundColor: 'rgba(0, 0, 0, 0)'
    },
    upperShadow: {
        shadowColor: "#000000",
        shadowRadius: 2,
        shadowOpacity: 1,
        shadowOffset: {
            height: 1,
            weight: 0
        },
        height: 1,
        backgroundColor: 'white'
    },
    shadow: {
        shadowColor: "#000000",
        shadowOpacity: 0.2,
        shadowRadius: 2,
        shadowOffset: {
            height: 4,
            width: 0
        },
    }
});

export default SetForm;
<|endoftext|>


<commit_before>(function()
{
	angular
		.module('settings')
		.factory('SettingsService', SettingsService);

	SettingsService.$inject = ['$state', '$ionicPopup', '$ionicHistory', 'localStorage'];

	function SettingsService($state, $ionicPopup, $ionicHistory, localStorage)
	{
		var service = {
			//App Version
			version						: '0.67',

			//Properties
			addGameClear			: setting('addGameClear', false),
			addGameFilter			: setting('addGameFilter', false),
			addGameNames			: setting('addGameNames', false),
			addGameRecents		: setting('addGameRecents', true),
			addGamePicker 		: setting('addGamePicker', gamePicker()),
			addGameSelect			: setting('addGameSelect', false),
			eloChartGames			: localStorage.getObject('eloChartGames', 30),
			isAdmin						: setting('isAdmin', false),
			league						: localStorage.getObject('league', undefined),
			leagues						: localStorage.getArray('leagues'),
			loggedIn					: userIsLoggedIn(),
			noGamePlayers			: setting('noGamePlayers', true),
			playerID					: localStorage.getObject('playerID', undefined),
			recentGames				: localStorage.getObject('recentGames', 3),
			showBadges				: setting('showBadges', true),
			showElo						: setting('showElo', true),
			showRelTimes			: setting('showRelTimes', true),
			useLocalDb				: setting('useLocalDb', isLocalhost()),
			//Functions
			isLocalhost				: isLocalhost,
			logIn 						: logIn,
			logOut						: logOut,
			reallyLogOut			: reallyLogOut,
			setProperty				: setProperty
		}

		return service;

		// Quick hack before team settings
		// Only league 9 should default to score picker
		function gamePicker()
		{
			return localStorage.getObject('league').leagueID === 9;
		}

		function isLocalhost()
    {
      return window.location.hostname === 'localhost';
    }

    function userIsLoggedIn()
    {
    	return _.isInteger(localStorage.getObject('league').leagueID) &&
    				 localStorage.getArray('leagues').length > 0;
    }

		function logIn(league)
		{
			// set the leagueID to localStorage
			setProperty('league', league);
			setProperty('playerID', league.player.playerID);
			setProperty('isAdmin', league.player.admin);
			service.loggedIn = true;
			addLeague(league);

			// Clear the entire history
			$ionicHistory.clearHistory();
			$ionicHistory.nextViewOptions({
        disableBack: true
      });

      // Hack in score picker setting
      setProperty('addGamePicker', gamePicker());

      clearCache();

			$state.go('app.leaderboard');
		}

		function logOut()
		{
			$ionicPopup.confirm({
        title: 'Change League',
        template: '
Are you sure you want to go back to the login screen?
'
      }).then(function(positive) {
        if(positive) {
          reallyLogOut();
        }
      });
		}

		function reallyLogOut()
		{
			$ionicHistory.nextViewOptions({
        disableBack: true
      });

			// remove the league from local storage
      removeLeague(service.league);
			setProperty('league', undefined);

			// Completely log out player
			service.loggedIn = false;
			setProperty('isAdmin', false);
			setProperty('playerID', undefined);

			clearCache();

      $state.go('login');
		}

		function clearCache()
		{
			setProperty('elos', undefined);
			setProperty('playerBadges', undefined);
			setProperty('winRates', undefined);
			setProperty('history', undefined);
			setProperty('players', undefined);
		}

		function addLeague(league)
		{
			if (!_.includes(_.map(service.leagues, 'leagueID'), league.leagueID))
			{
				service.leagues.push(league);
				setProperty('leagues', service.leagues);
			}
		}

		function removeLeague(league)
		{
			var leagueIndex = _.indexOf(_.map(service.leagues, 'leagueID'), league.leagueID);
			if (leagueIndex > -1)
			{
				service.leagues.splice(leagueIndex, 1);
				setProperty('leagues', service.leagues);
			}
		}

		function setProperty(property, value)
		{
			service[property] = value;
			localStorage.setObject(property, value);
		}

		function setting(property, defaultValue)
		{
			var setValue = localStorage.getObject(property);
			return _.isBoolean(setValue) ? setValue : defaultValue;
		}
	}
})();
<commit_msg>Clear recent players too<commit_after>(function()
{
	angular
		.module('settings')
		.factory('SettingsService', SettingsService);

	SettingsService.$inject = ['$state', '$ionicPopup', '$ionicHistory', 'localStorage'];

	function SettingsService($state, $ionicPopup, $ionicHistory, localStorage)
	{
		var service = {
			//App Version
			version						: '0.67',

			//Properties
			addGameClear			: setting('addGameClear', false),
			addGameFilter			: setting('addGameFilter', false),
			addGameNames			: setting('addGameNames', false),
			addGameRecents		: setting('addGameRecents', true),
			addGamePicker 		: setting('addGamePicker', gamePicker()),
			addGameSelect			: setting('addGameSelect', false),
			eloChartGames			: localStorage.getObject('eloChartGames', 30),
			isAdmin						: setting('isAdmin', false),
			league						: localStorage.getObject('league', undefined),
			leagues						: localStorage.getArray('leagues'),
			loggedIn					: userIsLoggedIn(),
			noGamePlayers			: setting('noGamePlayers', true),
			playerID					: localStorage.getObject('playerID', undefined),
			recentGames				: localStorage.getObject('recentGames', 3),
			showBadges				: setting('showBadges', true),
			showElo						: setting('showElo', true),
			showRelTimes			: setting('showRelTimes', true),
			useLocalDb				: setting('useLocalDb', isLocalhost()),
			//Functions
			isLocalhost				: isLocalhost,
			logIn 						: logIn,
			logOut						: logOut,
			reallyLogOut			: reallyLogOut,
			setProperty				: setProperty
		}

		return service;

		// Quick hack before team settings
		// Only league 9 should default to score picker
		function gamePicker()
		{
			return localStorage.getObject('league').leagueID === 9;
		}

		function isLocalhost()
    {
      return window.location.hostname === 'localhost';
    }

    function userIsLoggedIn()
    {
    	return _.isInteger(localStorage.getObject('league').leagueID) &&
    				 localStorage.getArray('leagues').length > 0;
    }

		function logIn(league)
		{
			// set the leagueID to localStorage
			setProperty('league', league);
			setProperty('playerID', league.player.playerID);
			setProperty('isAdmin', league.player.admin);
			service.loggedIn = true;
			addLeague(league);

			// Clear the entire history
			$ionicHistory.clearHistory();
			$ionicHistory.nextViewOptions({
        disableBack: true
      });

      // Hack in score picker setting
      setProperty('addGamePicker', gamePicker());

      clearCache();

			$state.go('app.leaderboard');
		}

		function logOut()
		{
			$ionicPopup.confirm({
        title: 'Change League',
        template: '
Are you sure you want to go back to the login screen?
'
      }).then(function(positive) {
        if(positive) {
          reallyLogOut();
        }
      });
		}

		function reallyLogOut()
		{
			$ionicHistory.nextViewOptions({
        disableBack: true
      });

			// remove the league from local storage
      removeLeague(service.league);
			setProperty('league', undefined);

			// Completely log out player
			service.loggedIn = false;
			setProperty('isAdmin', false);
			setProperty('playerID', undefined);

			clearCache();

      $state.go('login');
		}

		function clearCache()
		{
			setProperty('elos', undefined);
			setProperty('playerBadges', undefined);
			setProperty('winRates', undefined);
			setProperty('history', undefined);
			setProperty('players', undefined);
			setProperty('recentPlayers', undefined);
		}

		function addLeague(league)
		{
			if (!_.includes(_.map(service.leagues, 'leagueID'), league.leagueID))
			{
				service.leagues.push(league);
				setProperty('leagues', service.leagues);
			}
		}

		function removeLeague(league)
		{
			var leagueIndex = _.indexOf(_.map(service.leagues, 'leagueID'), league.leagueID);
			if (leagueIndex > -1)
			{
				service.leagues.splice(leagueIndex, 1);
				setProperty('leagues', service.leagues);
			}
		}

		function setProperty(property, value)
		{
			service[property] = value;
			localStorage.setObject(property, value);
		}

		function setting(property, defaultValue)
		{
			var setValue = localStorage.getObject(property);
			return _.isBoolean(setValue) ? setValue : defaultValue;
		}
	}
})();
<|endoftext|>


<commit_before>
import {EventEmitter} from 'events'

import invariant from 'invariant'
import uuid from 'node-uuid'

import {log, opts} from './log'

import * as actions from '../../actions'
import download from '../../tasks/download'

export async function startDownload (store, downloadOpts) {
  invariant(typeof store === 'object', 'startDownload must have a store')
  invariant(downloadOpts, 'startDownload cannot have null opts')
  invariant(downloadOpts.reason, 'startDownload must have a reason')
  invariant(downloadOpts.game, 'startDownload must have a game')
  invariant(typeof downloadOpts.totalSize === 'number', 'startDownload must have a total size')

  const existing = store.getState().downloads.downloadsByGameId[downloadOpts.game.id]
  if (existing && !existing.finished) {
    log(opts, `Not starting another download for ${downloadOpts.game.title}`)
    store.dispatch(actions.navigate('downloads'))
    return
  }

  const {upload, downloadKey} = downloadOpts
  log(opts, `Should download ${upload.id}, has dl key ? ${!!downloadKey}`)

  const id = uuid.v4()
  // FIXME: wasteful but easy
  store.dispatch(actions.downloadStarted({id, ...downloadOpts, downloadOpts}))

  let err
  try {
    const out = new EventEmitter()
    out.on('progress', (progress) => {
      store.dispatch(actions.downloadProgress({id, progress}))
      store.dispatch(actions.setProgress(progress))
    })

    const credentials = store.getState().session.credentials
    const extendedOpts = {
      ...opts,
      ...downloadOpts,
      credentials
    }

    log(opts, 'Starting download...')
    store.dispatch(actions.setProgress(0))
    await download(out, extendedOpts)
  } catch (e) {
    log(opts, 'Download threw')
    err = e.task || e
  } finally {
    err = err ? err.message || err : null
    log(opts, `Download ended, err: ${err || '<none>'}`)
    store.dispatch(actions.downloadEnded({id, err, downloadOpts}))
    store.dispatch(actions.setProgress(-1))
  }

  log(opts, 'Download done!')
}
<commit_msg>throttle progress<commit_after>
import {EventEmitter} from 'events'

import invariant from 'invariant'
import uuid from 'node-uuid'

import {log, opts} from './log'

import * as actions from '../../actions'
import download from '../../tasks/download'

import {throttle} from 'underline'

export async function startDownload (store, downloadOpts) {
  invariant(typeof store === 'object', 'startDownload must have a store')
  invariant(downloadOpts, 'startDownload cannot have null opts')
  invariant(downloadOpts.reason, 'startDownload must have a reason')
  invariant(downloadOpts.game, 'startDownload must have a game')
  invariant(typeof downloadOpts.totalSize === 'number', 'startDownload must have a total size')

  const existing = store.getState().downloads.downloadsByGameId[downloadOpts.game.id]
  if (existing && !existing.finished) {
    log(opts, `Not starting another download for ${downloadOpts.game.title}`)
    store.dispatch(actions.navigate('downloads'))
    return
  }

  const {upload, downloadKey} = downloadOpts
  log(opts, `Should download ${upload.id}, has dl key ? ${!!downloadKey}`)

  const id = uuid.v4()
  // FIXME: wasteful but easy
  store.dispatch(actions.downloadStarted({id, ...downloadOpts, downloadOpts}))

  let err
  try {
    const out = new EventEmitter()
    out.on('progress', ((progress) => {
      store.dispatch(actions.downloadProgress({id, progress}))
      store.dispatch(actions.setProgress(progress))
    })::throttle(250))

    const credentials = store.getState().session.credentials
    const extendedOpts = {
      ...opts,
      ...downloadOpts,
      credentials
    }

    log(opts, 'Starting download...')
    store.dispatch(actions.setProgress(0))
    await download(out, extendedOpts)
  } catch (e) {
    log(opts, 'Download threw')
    err = e.task || e
  } finally {
    err = err ? err.message || err : null
    log(opts, `Download ended, err: ${err || '<none>'}`)
    store.dispatch(actions.downloadEnded({id, err, downloadOpts}))
    store.dispatch(actions.setProgress(-1))
  }

  log(opts, 'Download done!')
}
<|endoftext|>


<commit_before>var util = requi re('../util');
var sql = require('mssql');

var querySelectId = 'SELECT id FROM Users WHERE sid=@sid;';
var queryInsertSid = 'INSERT INTO Users (sid) values (@sid);';

var tranDone = false;

var api = {
	post: function (req, res, next) {
		var conn = new sql.Connection(util.sqlConfiguration);
		conn.connect(function(err) {
			if (err) { completeError(err, res); return; }
			var tran = new sql.Transaction(conn);
			tran.begin(function(err) {
				if (err) { rollback500(err, res, tran); return; }
				var sqlRequest = new sql.Request(tran);
				var psSelectId = new sql.PreparedStatement(conn);
				psSelectId.input('sid', sql.NVarChar);
				psSelectId.prepare(querySelectId, function(err) {
					if (err) { rollback500(err, res, tran); return; }
					psSelectId.execute({ sid: req.azureMobile.user.id },
						function(err, recordset, affected) {
							if (err) { rollback500(err, res, tran); return; }
							if (typeof recordset != 'undefined' && recordset[0] != null) { rollback200(res, tran); return; }
							psSelectId.unprepare();
								/*
							userName(req.azureMobile.user, function() {
								var psInsertSid = new sql.PreparedStatement(conn);
								psInsertSid.input('sid', sql.NVarChar);
								psInsertSid.prepare(queryInsertSid, function(err) {
									if (err) { rollback500(err, res, tran); return; }
									psInsertSid.execute({ sid: req.azureMobile.user.id },
										function(err, recordset, affected) {
											if (err) { rollback500(err, res, tran); return; }
											commit200(res, tran);
										}
									);
								});
							});
								*/
						}
					);
				});
			});
		});
    }
};

api.access = 'authenticated';

var userName = function(user, next) {
    user.getIdentity().then(function(_identity){
    	console.log(_identity);
    	/*
    	next();
    	*/
    });
};

var completeError = function(err, res) {
	if (err) {
		console.error(err);
		if (res) res.sendStatus(500);
	}
}

var completetran = function(err, data) {
	if (err) {
		console.log(err);
		if (!data) return;
		if (!data.tranDone) {
			data.tranDone = true;
			if (data.action && util.isFunction(data.action)) data.action();
			if (data.res && data.sendStatus && util.isFunction(data.res.sendStatus)) data.res.sendStatus(data.sendStatus);
		}
	}
}

var rollback500 = function(err, res, tran) {
	completetran(err, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 500
	});
}

var rollback200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 200
	});
}

var commit200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.commit,
		res: res,
		sendStatus: 200
	});
}

module.exports = api;
<commit_msg>another<commit_after>var util = requi re('../util');
var sql = require('mssql');

var querySelectId = 'SELECT id FROM Users WHERE sid=@sid;';
var queryInsertSid = 'INSERT INTO Users (sid) values (@sid);';

var tranDone = false;

var api = {
	post: function (req, res, next) {
		var conn = new sql.Connection(util.sqlConfiguration);
		conn.connect(function(err) {
			if (err) { completeError(err, res); return; }
			var tran = new sql.Transaction(conn);
			tran.begin(function(err) {
				if (err) { rollback500(err, res, tran); return; }
				var sqlRequest = new sql.Request(tran);
				var psSelectId = new sql.PreparedStatement(conn);
				psSelectId.input('sid', sql.NVarChar);
				psSelectId.prepare(querySelectId, function(err) {
					if (err) { rollback500(err, res, tran); return; }
					psSelectId.execute({ sid: req.azureMobile.user.id },
						function(err, recordset, affected) {
							if (err) { rollback500(err, res, tran); return; }
							if (typeof recordset != 'undefined' && recordset[0] != null) { rollback200(res, tran); return; }
							psSelectId.unprepare();
								/*
							userName(req.azureMobile.user, function() {
								var psInsertSid = new sql.PreparedStatement(conn);
								psInsertSid.input('sid', sql.NVarChar);
								psInsertSid.prepare(queryInsertSid, function(err) {
									if (err) { rollback500(err, res, tran); return; }
									psInsertSid.execute({ sid: req.azureMobile.user.id },
										function(err, recordset, affected) {
											if (err) { rollback500(err, res, tran); return; }
											commit200(res, tran);
										}
									);
								});
							});
								*/
						}
					);
				});
			});
		});
    }
};

api.access = 'authenticated';

/*
var userName = function(user, next) {
    user.getIdentity().then(function(_identity){
    	console.log(_identity);
    	next();
    });
};
*/

var completeError = function(err, res) {
	if (err) {
		console.error(err);
		if (res) res.sendStatus(500);
	}
}

var completetran = function(err, data) {
	if (err) {
		console.log(err);
		if (!data) return;
		if (!data.tranDone) {
			data.tranDone = true;
			if (data.action && util.isFunction(data.action)) data.action();
			if (data.res && data.sendStatus && util.isFunction(data.res.sendStatus)) data.res.sendStatus(data.sendStatus);
		}
	}
}

var rollback500 = function(err, res, tran) {
	completetran(err, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 500
	});
}

var rollback200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.rollback,
		res: res,
		sendStatus: 200
	});
}

var commit200 = function(res, tran) {
	completetran(undefined, {
		tranDone: tranDone,
		action: tran.commit,
		res: res,
		sendStatus: 200
	});
}

module.exports = api;
<|endoftext|>


<commit_before>var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

const AccountRootFlags = {
  PasswordSpent:   { name: 'password_spent', value: 0x00010000 },
  RequireDestTag:  { name: 'require_dest_tag', value: 0x00020000 },
  RequireAuth:     { name: 'require_auth', value: 0x00040000 },
  DisallowXRP:     { name: 'disallow_xrp', value: 0x00080000 },
  DisableMaster:   { name: 'disable_master', value: 0x00100000 }
}

const AccountRootFields = {
  EmailHash:      { name: 'email_hash' },
  WalletLocator:  { name: 'wallet_locator' },
  WalletSize:     { name: 'wallet_size' },
  MessageKey:     { name: 'message_key' },
  Domain:         { name: 'domain', encoding: 'hex' },
  TransferRate:   { name: 'transfer_rate' },
  Signers:        { name: 'signers' }
}

exports.get = getSettings;

function getSettings($, req, res, next) {
  var remote = $.remote;

  var opts = {
    account: req.params.account
  }

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter "account" is not a valid Ripple address' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function getAccountSettings(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'No connection to rippled' });
    }

    remote.requestAccountInfo(opts.account, function(err, info) {
      if (err) return callback(err);

      var settings = { };
      var data = info.account_data;

      for (var flagName in AccountRootFlags) {
        var flag = AccountRootFlags[flagName];
        settings[flag.name] = Boolean(data.Flags & flag.value);
      }

      for (var fieldName in AccountRootFields) {
        if (!(fieldName in data)) continue;

        var field = AccountRootFields[fieldName];
        var value = data[fieldName];

        if (field.encoding === 'hex') {
          value = new Buffer(value, 'hex').toString();
        }

        settings[field.name] = value;
      }

      callback(null, settings);
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, settings: settings });
    }
  });
};

exports.change = changeSettings;

function changeSettings($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return callback(new Error('Parameter "account" is not a valid Ripple address'));
    }

    if (!opts.secret) {
      return callback(new Error('Parameter "secret" is not a valid Ripple secret'));
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function changeAccountSettings(connected, callback) {
    var transaction = remote.transaction().accountSet(opts.account);
    transaction.secret(opts.secret);

    var FlagSet = {
      require_dest_tag: {
        unset: 'OptionalDestTag',
        set: 'RequireDestTag',
      },
      require_auth: {
        unset: 'OptionalAuth',
        set: 'RequireAuth'
      },
      disallow_xrp: {
        unset: 'AllowXRP',
        set: 'DisallowXRP'
      }
    }

    Object.keys(FlagSet).forEach(function(flagName) {
      if (!(flagName in opts)) return

      var flag = FlagSet[flagName];
      var value = opts[flagName];

      if (typeof value !== 'boolean') {
        return callback(new TypeError('Parameter is not boolean: ' + flagName));
      }

      transaction.setFlags(value ? flag.set : flag.unset);
    });

    Object.keys(AccountRootFields).forEach(function(fieldName) {
      var field = AccountRootFields[fieldName];
      var value = opts[field.name];

      if (typeof value === 'undefined') return;

      if (field.encoding === 'hex') {
        value = new Buffer(value).toString('hex');
      }

      transaction.tx_json[fieldName] = value;
    });

    transaction.submit(callback);
  };

  var steps = [
    validateOptions,
    ensureConnected,
    changeAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      getSettings($, req, res, next);
    }
  });
};
<commit_msg>api/settings/change HTTP status codes<commit_after>var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

const AccountRootFlags = {
  PasswordSpent:   { name: 'password_spent', value: 0x00010000 },
  RequireDestTag:  { name: 'require_dest_tag', value: 0x00020000 },
  RequireAuth:     { name: 'require_auth', value: 0x00040000 },
  DisallowXRP:     { name: 'disallow_xrp', value: 0x00080000 },
  DisableMaster:   { name: 'disable_master', value: 0x00100000 }
}

const AccountRootFields = {
  EmailHash:      { name: 'email_hash' },
  WalletLocator:  { name: 'wallet_locator' },
  WalletSize:     { name: 'wallet_size' },
  MessageKey:     { name: 'message_key' },
  Domain:         { name: 'domain', encoding: 'hex' },
  TransferRate:   { name: 'transfer_rate' },
  Signers:        { name: 'signers' }
}

exports.get = getSettings;

function getSettings($, req, res, next) {
  var remote = $.remote;

  var opts = {
    account: req.params.account
  }

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter "account" is not a valid Ripple address' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function getAccountSettings(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'No connection to rippled' });
    }

    remote.requestAccountInfo(opts.account, function(err, info) {
      if (err) return callback(err);

      var settings = { };
      var data = info.account_data;

      for (var flagName in AccountRootFlags) {
        var flag = AccountRootFlags[flagName];
        settings[flag.name] = Boolean(data.Flags & flag.value);
      }

      for (var fieldName in AccountRootFields) {
        if (!(fieldName in data)) continue;

        var field = AccountRootFields[fieldName];
        var value = data[fieldName];

        if (field.encoding === 'hex') {
          value = new Buffer(value, 'hex').toString();
        }

        settings[field.name] = value;
      }

      callback(null, settings);
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, settings: settings });
    }
  });
};

exports.change = changeSettings;

function changeSettings($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter "account" is not a valid Ripple address' });
    }

    if (!opts.secret) {
      return res.json(400, { success: false, 'Parameter "secret" is not a valid Ripple secret' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function changeAccountSettings(connected, callback) {
    var transaction = remote.transaction().accountSet(opts.account);
    transaction.secret(opts.secret);

    var FlagSet = {
      require_dest_tag: {
        unset: 'OptionalDestTag',
        set: 'RequireDestTag',
      },
      require_auth: {
        unset: 'OptionalAuth',
        set: 'RequireAuth'
      },
      disallow_xrp: {
        unset: 'AllowXRP',
        set: 'DisallowXRP'
      }
    }

    Object.keys(FlagSet).forEach(function(flagName) {
      if (!(flagName in opts)) return

      var flag = FlagSet[flagName];
      var value = opts[flagName];

      if (typeof value !== 'boolean') {
        return callback(new TypeError('Parameter is not boolean: ' + flagName));
      }

      transaction.setFlags(value ? flag.set : flag.unset);
    });

    Object.keys(AccountRootFields).forEach(function(fieldName) {
      var field = AccountRootFields[fieldName];
      var value = opts[field.name];

      if (typeof value === 'undefined') return;

      if (field.encoding === 'hex') {
        value = new Buffer(value).toString('hex');
      }

      transaction.tx_json[fieldName] = value;
    });

    transaction.submit(callback);
  };

  var steps = [
    validateOptions,
    ensureConnected,
    changeAccountSettings
  ]

  async.waterfall(steps, function(err, settings) {
    if (err) {
      next(err);
    } else {
      getSettings($, req, res, next);
    }
  });
};
<|endoftext|>


<commit_before><commit_msg>must lock a PointerupButton when it's monotouchable<commit_after><|endoftext|>


<commit_before>import React from 'react';
import Paper from 'material-ui/Paper';
import { Glyphicon } from "react-bootstrap";

import SearchForProvider from './SearchForActivity';
import ActivityPicker from './ActivityPicker';
import SuitedForPicker from './SuitedForPicker';

import '../../styles/activityFilters.css'

class ProviderFilters extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            tButtonClicked: true,
        };
    };

    handleEmptyFilter = () => {
        this.props.onButtonChange(this.state.tButtonClicked);
    };

    render() {

        const providerName = this.props.providersForSearch.map(provider => provider.Navn);

        return (
            <Paper className="filter-container">

                            Tøm filter

                        <button type="button" className="btn btn-warning" id="button-trash"
                                onClick={this.handleEmptyFilter}>
                            <Glyphicon glyph="glyphicon glyphicon-trash"/>
                        </button>

Finn arrangører

                <SearchForProvider
                    onFilterChange={this.props.onSearchForChange}
                    activitiesName={providerName}
                    placeholderText="Søk på arrangør..."
                    searchForFilter={this.props.searchForFilter}
                />

                        <SuitedForPicker
                            onFilterChange={this.props.onSuitedForFilterChange}
                            activeFilters={this.props.suitedForFilters}
                            suitedForButton={this.props.suitedForButton}
                        />

                        <ActivityPicker
                            onFilterChange={this.props.onActivityFilterChange}
                            activeFilters={this.props.activityFilters}
                            activityButton={this.props.activityButton}
                        />

            </Paper>
        )
    };
}
;

ProviderFilters.propTypes = {
    onSearchForChange: React.PropTypes.func.isRequired,
    providersForSearch: React.PropTypes.array.isRequired,
    searchForFilter: React.PropTypes.string.isRequired,

    onSuitedForFilterChange: React.PropTypes.func.isRequired,
    suitedForFilters: React.PropTypes.array.isRequired,
    suitedForButton: React.PropTypes.func.isRequired,

    onActivityFilterChange: React.PropTypes.func.isRequired,
    activityFilters: React.PropTypes.array.isRequired,
    activityButton: React.PropTypes.func.isRequired,

    onButtonChange: React.PropTypes.func.isRequired,
};

export default ProviderFilters;
<commit_msg>cleaned providerFilters<commit_after>import React from 'react';
import Paper from 'material-ui/Paper';
import {Glyphicon} from "react-bootstrap";
import SearchForProvider from './SearchForActivity';
import ActivityPicker from './ActivityPicker';
import SuitedForPicker from './SuitedForPicker';
import '../../styles/activityFilters.css'

class ProviderFilters extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            tButtonClicked: true,
        };
    };

    handleEmptyFilter = () => {
        this.props.onButtonChange(this.state.tButtonClicked);
    };

    render() {
        const providerName = this.props.providersForSearch.map(provider => provider.Navn);

        return (
            <Paper className="filter-container">

                            Tøm filter

                        <button type="button" className="btn btn-warning" id="button-trash"
                                onClick={this.handleEmptyFilter}>
                            <Glyphicon glyph="glyphicon glyphicon-trash"/>
                        </button>

Finn arrangører

                <SearchForProvider
                    onFilterChange={this.props.onSearchForChange}
                    activitiesName={providerName}
                    placeholderText="Søk på arrangør..."
                    searchForFilter={this.props.searchForFilter}
                />

                        <SuitedForPicker
                            onFilterChange={this.props.onSuitedForFilterChange}
                            activeFilters={this.props.suitedForFilters}
                            suitedForButton={this.props.suitedForButton}
                        />

                        <ActivityPicker
                            onFilterChange={this.props.onActivityFilterChange}
                            activeFilters={this.props.activityFilters}
                            activityButton={this.props.activityButton}
                        />

            </Paper>
        )
    };
};

ProviderFilters.propTypes = {
    onSearchForChange: React.PropTypes.func.isRequired,
    providersForSearch: React.PropTypes.array.isRequired,
    searchForFilter: React.PropTypes.string.isRequired,

    onSuitedForFilterChange: React.PropTypes.func.isRequired,
    suitedForFilters: React.PropTypes.array.isRequired,
    suitedForButton: React.PropTypes.func.isRequired,

    onActivityFilterChange: React.PropTypes.func.isRequired,
    activityFilters: React.PropTypes.array.isRequired,
    activityButton: React.PropTypes.func.isRequired,

    onButtonChange: React.PropTypes.func.isRequired,
};

export default ProviderFilters;
<|endoftext|>


<commit_before>/*!
 *  Chinachu Task Operator Service (chinachu-operator)
 *
 *  Copyright (c) 2012 Yuki KAN and Chinachu Project Contributors
 *  http://chinachu.akkar.in/
**/

var CONFIG_FILE         = __dirname + '/config.json';
var RESERVES_DATA_FILE  = __dirname + '/data/reserves.json';
var RECORDING_DATA_FILE = __dirname + '/data/recording.json';
var RECORDED_DATA_FILE  = __dirname + '/data/recorded.json';

// 標準モジュールのロード
var path          = require('path');
var fs            = require('fs');
var util          = require('util');
var child_process = require('child_process');

// ディレクトリチェック
if (!fs.existsSync('./data/') || !fs.existsSync('./log/') || !fs.existsSync('./web/')) {
	util.error('必要なディレクトリが存在しないか、カレントワーキングディレクトリが不正です。');
	process.exit(1);
}

// 終了処理
process.on('SIGQUIT', function() {
	setTimeout(function() {
		process.exit(0);
	}, 0);
});

// 追加モジュールのロード
var dateFormat = require('dateformat');
var OAuth      = require('oauth').OAuth;
var chinachu   = require('chinachu-common');

// 設定の読み込み
var config = require(CONFIG_FILE);

// ファイル更新監視: ./data/reserves.json
var reserves = [];
chinachu.jsonWatcher(
	RESERVES_DATA_FILE
	,
	function _onUpdated(err, data, mes) {
		if (err) {
			util.error(err);
			return;
		}

		reserves = data;
		util.log(mes);
	}
	,
	{ create: [], now: true }
);

// ファイル更新監視: ./data/recorded.json
var recorded = [];
chinachu.jsonWatcher(
	RECORDED_DATA_FILE
	,
	function _onUpdated(err, data, mes) {
		if (err) {
			util.error(err);
			return;
		}

		recorded = data;
		util.log(mes);
	}
	,
	{ create: [], now: true }
);

// 録画中リストをクリア
fs.writeFileSync(RECORDING_DATA_FILE, '[]');

// Tweeter (Experimental)
if (config.operTweeter && config.operTweeterAuth && config.operTweeterFormat) {
	var tweeter = new OAuth(
		'https://api.twitter.com/oauth/request_token',
		'https://api.twitter.com/oauth/access_token',
		config.operTweeterAuth.consumerKey,
		config.operTweeterAuth.consumerSecret,
		'1.0', null, 'HMAC-SHA1'
	);

	var tweeterUpdater = function(status) {
		tweeter.post(
			'http://api.twitter.com/1/statuses/update.json',
			config.operTweeterAuth.accessToken,
			config.operTweeterAuth.accessTokenSecret,
			{ status: status }, 'application/json',
			function _onUpdatedTweeter(err, data, res) {
				if (err) {
					util.log('[Tweeter] Error: ' + JSON.stringify(err));
				} else {
					util.log('[Tweeter] Updated: ' + status);
				}
			}
		);
	};
}

//
var schedulerProcessTime    = config.operSchedulerProcessTime    || 1000 * 60 * 30;
var schedulerIntervalTime   = config.operSchedulerIntervalTime   || 1000 * 60 * 60 * 2;
var schedulerSleepStartHour = config.operSchedulerSleepStartHour || 1;
var schedulerSleepEndHour   = config.operSchedulerSleepEndHour   || 5;
var prepTime    = config.operRecPrepTime    || 1000 * 60 * 1;
var offsetStart = config.operRecOffsetStart || 1000 * 5;
var offsetEnd   = config.operRecOffsetEnd   || -(1000 * 8);

var clock     = new Date().getTime();
var next      = 0;
var recording = [];
var scheduler = null;
var scheduled = 0;

var mainInterval = setInterval(main, 5000);
function main() {
	clock = new Date().getTime();

	if (reserves.length !== 0) {
		reserves.forEach(reservesChecker);
	} else {
		next = 0;
	}

	if (
		(scheduler === null) &&
		(clock - scheduled > schedulerIntervalTime) &&
		((next === 0) || (next - clock > schedulerProcessTime)) &&
		((schedulerSleepStartHour > new Date().getHours()) || (schedulerSleepEndHour <= new Date().getHours()))
	) {
		startScheduler();
		scheduled = clock;
	}
}

// 予約時間チェック
function reservesChecker(program, i) {
	// 予約時間超過
	if (clock > program.end) {
		next = 0;
		return;
	}

	// 予約準備時間内
	if (program.start - clock <= prepTime) {
		if (
			(isRecording(program) === false) &&
			(isRecorded(program) === false)
		) {
			prepRecord(program);
		}
	}

	// 次の開始時間
	if (next === 0) {
		next = program.start;
	}
}

// 録画中か
function isRecording(program) {
	for (var i = 0; i < recording.length; i++) {
		if (recording[i].id === program.id) {
			return true;
		}
	}

	return false;
}

// 録画したか
function isRecorded(program) {
	for (var i = 0; i < recorded.length; i++) {
		if (recorded[i].id === program.id) {
			return true;
		}
	}

	return false;
}

// スケジューラーを開始
function startScheduler() {
	if ((scheduler !== null) || (recording.length !== 0)) { return; }

	scheduler = child_process.spawn('node', [ 'app-scheduler.js', '-f' ]);
	util.log('SPAWN: node app-scheduler.js -f (pid=' + scheduler.pid + ')');

	// ログ用
	var output = fs.createWriteStream('./log/scheduler', {'flags': 'a'});
	util.log('STREAM: ./log/scheduler');

	scheduler.stdout.on('data', function(data) {
		try {
			output.write(data);
		} catch (e) {
			util.log('ERROR: Scheduler -> Abort (' + e + ')');
			finalize();
		}
	});

	function finalize() {
		process.removeListener('SIGINT', finalize);
		process.removeListener('SIGQUIT', finalize);
		process.removeListener('SIGTERM', finalize);

		try { output.end(); } catch (e) {}

		scheduler = null;
	}

	scheduler.on('exit', finalize);

	process.on('SIGINT', stopScheduler);
	process.on('SIGQUIT', stopScheduler);
	process.on('SIGTERM', stopScheduler);
}

// スケジューラーを停止
function stopScheduler() {
	if (scheduler === null) { return; }

	scheduler.kill('SIGQUIT');
	util.log('KILL: SIGQUIT -> Scheduler (pid=' + scheduler.pid + ')');
}

// 録画準備
function prepRecord(program) {
	util.log(
		'PREPARE: ' + dateFormat(new Date(program.start), 'isoDateTime') +
		' [' + program.channel.name + '] ' + program.title
	);

	recording.push(program);

	var timeout = program.start - clock - offsetStart;
	if (timeout < 0) { timeout = 3000; }

	setTimeout(function() {
		doRecord(program);
	}, timeout);

	fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
	util.log('WRITE: ' + RECORDING_DATA_FILE);

	if (scheduler !== null) {
		stopScheduler();
	}

	// Tweeter (Experimental)
	if ((timeout !== 0) && tweeter && config.operTweeterFormat.prepare) {
		tweeterUpdater(
			config.operTweeterFormat.prepare
			.replace('<id>', program.id)
			.replace('<type>', program.channel.type)
			.replace('<channel>', ((program.channel.type === 'CS') ? program.channel.sid : program.channel.channel))
			.replace('<title>',   program.title)
		);
	}
}

// 録画実行
function doRecord(program) {
	util.log(
		'RECORD: ' + dateFormat(new Date(program.start), 'isoDateTime') +
		' [' + program.channel.name + '] ' + program.title
	);

	var timeout = program.end - new Date().getTime() + offsetEnd;

	if (timeout < 0) {
		util.log('FATAL: 時間超過による録画中止');
		return;
	}

	// チューナーを選ぶ
	var tuner = chinachu.getFreeTunerSync(config.tuners, program.channel.type);

	// チューナーが見つからない
	if (tuner === null) {
		util.log('WARNING: ' + program.channel.type + ' 利用可能なチューナーが見つかりません (存在しないかロックされています) (5秒後に再試行)');
		setTimeout(function() {
			doRecord(program);
		}, 5000);
		return;
	}

	// チューナーをロック
	chinachu.lockTunerSync(tuner);
	util.log('LOCK: ' + tuner.name + ' (n=' + tuner.n + ')');

	program.tuner = tuner;

	// 保存先パス
	var recPath = config.recordedDir + formatRecordedName(program);
	program.recorded = recPath;

	// 録画コマンド
	var recCmd = tuner.command.replace('<sid>', program.channel.sid).replace('<channel>', program.channel.channel);
	program.command = recCmd;

	// 録画プロセスを生成
	var recProc = child_process.spawn(recCmd.split(' ')[0], recCmd.replace(/[^ ]+ /, '').split(' '));
	util.log('SPAWN: ' + recCmd + ' (pid=' + recProc.pid + ')');
	program.pid = recProc.pid;

	// タイムアウト
	setTimeout(function() { recProc.kill('SIGTERM'); }, timeout);

	// 状態保存
	fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
	util.log('WRITE: ' + RECORDING_DATA_FILE);

	// 書き込みストリームを作成
	var recFile = fs.createWriteStream(recPath);
	util.log('STREAM: ' + recPath);

	// ts出力
	recProc.stdout.pipe(recFile);

	// ログ出力
	recProc.stderr.on('data', function(data) {
		util.log('#' + (recCmd.split(' ')[0] + ': ' + data + '').replace(/\n/g, ' ').trim());
	});

	// お片付け
	function finalize() {
		process.removeListener('SIGINT', finalize);
		process.removeListener('SIGQUIT', finalize);
		process.removeListener('SIGTERM', finalize);
		recProc.stdout.removeAllListeners();

		// 書き込みストリームを閉じる
		recFile.end();

		// チューナーのロックを解除
		try {
			chinachu.unlockTunerSync(tuner);
			util.log('UNLOCK: ' + tuner.name + ' (n=' + tuner.n + ')');
		} catch(e) {
			util.log(e);
		}

		// 状態を更新
		delete program.pid;
		recorded.push(program);
		recording.splice(recording.indexOf(program), 1);
		fs.writeFileSync(RECORDED_DATA_FILE, JSON.stringify(recorded));
		fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
		util.log('WRITE: ' + RECORDED_DATA_FILE);
		util.log('WRITE: ' + RECORDING_DATA_FILE);
	}
	// 録画プロセス終了時処理
	recProc.on('exit', finalize);

	// 終了シグナル時処理
	process.on('SIGINT', finalize);
	process.on('SIGQUIT', finalize);
	process.on('SIGTERM', finalize);

	// Tweeter (Experimental)
	if (tweeter && config.operTweeterFormat.start) {
		tweeterUpdater(
			config.operTweeterFormat.start
			.replace('<id>', program.id)
			.replace('<type>', program.channel.type)
			.replace('<channel>', ((program.channel.type === 'CS') ? program.channel.sid : program.channel.channel))
			.replace('<title>',   program.title)
		);
	}
}

// 録画ファイル名
function formatRecordedName(program) {
	var name = config.recordedFormat;

	// <date:?>
	if (name.match(/<date:[^>]+>/) !== null) {
		name = name.replace(/<date:[^>]+>/, dateFormat(new Date(program.start), name.match(/<date:([^>]+)>/)[1]));
	}

	// <id>
	name = name.replace('<id>', program.id);

	// <type>
	name = name.replace('<type>', program.channel.type);

	// <channel>
	name = name.replace('<channel>', (program.channel.type === 'CS') ? program.channel.sid : program.channel.channel);

	// <tuner>
	name = name.replace('<tuner>', program.tuner.name);

	// <title>
	name = name.replace('<title>', program.title);

	// strip
	name = name.replace(/\//g, '／').replace(/\\/g, '＼').replace(/:/g, '：').replace(/\*/g, '＊').replace(/\?/g, '？');
	name = name.replace(/"/g, '”').replace(/</g, '＜').replace(/>/g, '＞').replace(/\|/g, '｜').replace(/≫/g, '＞＞');

	return name;
}
<commit_msg>syntax correction<commit_after>/*!
 *  Chinachu Task Operator Service (chinachu-operator)
 *
 *  Copyright (c) 2012 Yuki KAN and Chinachu Project Contributors
 *  http://chinachu.akkar.in/
**/

var CONFIG_FILE         = __dirname + '/config.json';
var RESERVES_DATA_FILE  = __dirname + '/data/reserves.json';
var RECORDING_DATA_FILE = __dirname + '/data/recording.json';
var RECORDED_DATA_FILE  = __dirname + '/data/recorded.json';

// 標準モジュールのロード
var path          = require('path');
var fs            = require('fs');
var util          = require('util');
var child_process = require('child_process');

// ディレクトリチェック
if (!fs.existsSync('./data/') || !fs.existsSync('./log/') || !fs.existsSync('./web/')) {
	util.error('必要なディレクトリが存在しないか、カレントワーキングディレクトリが不正です。');
	process.exit(1);
}

// 終了処理
process.on('SIGQUIT', function() {
	setTimeout(function() {
		process.exit(0);
	}, 0);
});

// 追加モジュールのロード
var dateFormat = require('dateformat');
var OAuth      = require('oauth').OAuth;
var chinachu   = require('chinachu-common');

// 設定の読み込み
var config = require(CONFIG_FILE);

// ファイル更新監視: ./data/reserves.json
var reserves = [];
chinachu.jsonWatcher(
	RESERVES_DATA_FILE
	,
	function _onUpdated(err, data, mes) {
		if (err) {
			util.error(err);
			return;
		}

		reserves = data;
		util.log(mes);
	}
	,
	{ create: [], now: true }
);

// ファイル更新監視: ./data/recorded.json
var recorded = [];
chinachu.jsonWatcher(
	RECORDED_DATA_FILE
	,
	function _onUpdated(err, data, mes) {
		if (err) {
			util.error(err);
			return;
		}

		recorded = data;
		util.log(mes);
	}
	,
	{ create: [], now: true }
);

// 録画中リストをクリア
fs.writeFileSync(RECORDING_DATA_FILE, '[]');

// Tweeter (Experimental)
if (config.operTweeter && config.operTweeterAuth && config.operTweeterFormat) {
	var tweeter = new OAuth(
		'https://api.twitter.com/oauth/request_token',
		'https://api.twitter.com/oauth/access_token',
		config.operTweeterAuth.consumerKey,
		config.operTweeterAuth.consumerSecret,
		'1.0', null, 'HMAC-SHA1'
	);

	var tweeterUpdater = function(status) {
		tweeter.post(
			'http://api.twitter.com/1/statuses/update.json',
			config.operTweeterAuth.accessToken,
			config.operTweeterAuth.accessTokenSecret,
			{ status: status }, 'application/json',
			function _onUpdatedTweeter(err, data, res) {
				if (err) {
					util.log('[Tweeter] Error: ' + JSON.stringify(err));
				} else {
					util.log('[Tweeter] Updated: ' + status);
				}
			}
		);
	};
}

//
var schedulerProcessTime    = config.operSchedulerProcessTime    || 1000 * 60 * 30;
var schedulerIntervalTime   = config.operSchedulerIntervalTime   || 1000 * 60 * 60 * 2;
var schedulerSleepStartHour = config.operSchedulerSleepStartHour || 1;
var schedulerSleepEndHour   = config.operSchedulerSleepEndHour   || 5;
var prepTime    = config.operRecPrepTime    || 1000 * 60 * 1;
var offsetStart = config.operRecOffsetStart || 1000 * 5;
var offsetEnd   = config.operRecOffsetEnd   || -(1000 * 8);

var clock     = new Date().getTime();
var next      = 0;
var recording = [];
var scheduler = null;
var scheduled = 0;

var mainInterval = setInterval(main, 5000);
function main() {
	clock = new Date().getTime();

	if (reserves.length !== 0) {
		reserves.forEach(reservesChecker);
	} else {
		next = 0;
	}

	if (
		(scheduler === null) &&
		(clock - scheduled > schedulerIntervalTime) &&
		((next === 0) || (next - clock > schedulerProcessTime)) &&
		((schedulerSleepStartHour > new Date().getHours()) || (schedulerSleepEndHour <= new Date().getHours()))
	) {
		startScheduler();
		scheduled = clock;
	}
}

// 予約時間チェック
function reservesChecker(program, i) {
	// 予約時間超過
	if (clock > program.end) {
		next = 0;
		return;
	}

	// 予約準備時間内
	if (program.start - clock <= prepTime) {
		if (
			(isRecording(program) === false) &&
			(isRecorded(program) === false)
		) {
			prepRecord(program);
		}
	}

	// 次の開始時間
	if (next === 0) {
		next = program.start;
	}
}

// 録画中か
function isRecording(program) {
	for (var i = 0; i < recording.length; i++) {
		if (recording[i].id === program.id) {
			return true;
		}
	}

	return false;
}

// 録画したか
function isRecorded(program) {
	for (var i = 0; i < recorded.length; i++) {
		if (recorded[i].id === program.id) {
			return true;
		}
	}

	return false;
}

// スケジューラーを開始
function startScheduler() {
	if ((scheduler !== null) || (recording.length !== 0)) { return; }

	scheduler = child_process.spawn('node', [ 'app-scheduler.js', '-f' ]);
	util.log('SPAWN: node app-scheduler.js -f (pid=' + scheduler.pid + ')');

	// ログ用
	var output = fs.createWriteStream('./log/scheduler', { flags: 'a' });
	util.log('STREAM: ./log/scheduler');

	scheduler.stdout.on('data', function(data) {
		try {
			output.write(data);
		} catch (e) {
			util.log('ERROR: Scheduler -> Abort (' + e + ')');
			finalize();
		}
	});

	function finalize() {
		process.removeListener('SIGINT', finalize);
		process.removeListener('SIGQUIT', finalize);
		process.removeListener('SIGTERM', finalize);

		try { output.end(); } catch (e) {}

		scheduler = null;
	}

	scheduler.on('exit', finalize);

	process.on('SIGINT', stopScheduler);
	process.on('SIGQUIT', stopScheduler);
	process.on('SIGTERM', stopScheduler);
}

// スケジューラーを停止
function stopScheduler() {
	if (scheduler === null) { return; }

	scheduler.kill('SIGQUIT');
	util.log('KILL: SIGQUIT -> Scheduler (pid=' + scheduler.pid + ')');
}

// 録画準備
function prepRecord(program) {
	util.log(
		'PREPARE: ' + dateFormat(new Date(program.start), 'isoDateTime') +
		' [' + program.channel.name + '] ' + program.title
	);

	recording.push(program);

	var timeout = program.start - clock - offsetStart;
	if (timeout < 0) { timeout = 3000; }

	setTimeout(function() {
		doRecord(program);
	}, timeout);

	fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
	util.log('WRITE: ' + RECORDING_DATA_FILE);

	if (scheduler !== null) {
		stopScheduler();
	}

	// Tweeter (Experimental)
	if ((timeout !== 0) && tweeter && config.operTweeterFormat.prepare) {
		tweeterUpdater(
			config.operTweeterFormat.prepare
			.replace('<id>', program.id)
			.replace('<type>', program.channel.type)
			.replace('<channel>', ((program.channel.type === 'CS') ? program.channel.sid : program.channel.channel))
			.replace('<title>',   program.title)
		);
	}
}

// 録画実行
function doRecord(program) {
	util.log(
		'RECORD: ' + dateFormat(new Date(program.start), 'isoDateTime') +
		' [' + program.channel.name + '] ' + program.title
	);

	var timeout = program.end - new Date().getTime() + offsetEnd;

	if (timeout < 0) {
		util.log('FATAL: 時間超過による録画中止');
		return;
	}

	// チューナーを選ぶ
	var tuner = chinachu.getFreeTunerSync(config.tuners, program.channel.type);

	// チューナーが見つからない
	if (tuner === null) {
		util.log('WARNING: ' + program.channel.type + ' 利用可能なチューナーが見つかりません (存在しないかロックされています) (5秒後に再試行)');
		setTimeout(function() {
			doRecord(program);
		}, 5000);
		return;
	}

	// チューナーをロック
	chinachu.lockTunerSync(tuner);
	util.log('LOCK: ' + tuner.name + ' (n=' + tuner.n + ')');

	program.tuner = tuner;

	// 保存先パス
	var recPath = config.recordedDir + formatRecordedName(program);
	program.recorded = recPath;

	// 録画コマンド
	var recCmd = tuner.command.replace('<sid>', program.channel.sid).replace('<channel>', program.channel.channel);
	program.command = recCmd;

	// 録画プロセスを生成
	var recProc = child_process.spawn(recCmd.split(' ')[0], recCmd.replace(/[^ ]+ /, '').split(' '));
	util.log('SPAWN: ' + recCmd + ' (pid=' + recProc.pid + ')');
	program.pid = recProc.pid;

	// タイムアウト
	setTimeout(function() { recProc.kill('SIGTERM'); }, timeout);

	// 状態保存
	fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
	util.log('WRITE: ' + RECORDING_DATA_FILE);

	// 書き込みストリームを作成
	var recFile = fs.createWriteStream(recPath);
	util.log('STREAM: ' + recPath);

	// ts出力
	recProc.stdout.pipe(recFile);

	// ログ出力
	recProc.stderr.on('data', function(data) {
		util.log('#' + (recCmd.split(' ')[0] + ': ' + data + '').replace(/\n/g, ' ').trim());
	});

	// お片付け
	function finalize() {
		process.removeListener('SIGINT', finalize);
		process.removeListener('SIGQUIT', finalize);
		process.removeListener('SIGTERM', finalize);
		recProc.stdout.removeAllListeners();

		// 書き込みストリームを閉じる
		recFile.end();

		// チューナーのロックを解除
		try {
			chinachu.unlockTunerSync(tuner);
			util.log('UNLOCK: ' + tuner.name + ' (n=' + tuner.n + ')');
		} catch(e) {
			util.log(e);
		}

		// 状態を更新
		delete program.pid;
		recorded.push(program);
		recording.splice(recording.indexOf(program), 1);
		fs.writeFileSync(RECORDED_DATA_FILE, JSON.stringify(recorded));
		fs.writeFileSync(RECORDING_DATA_FILE, JSON.stringify(recording));
		util.log('WRITE: ' + RECORDED_DATA_FILE);
		util.log('WRITE: ' + RECORDING_DATA_FILE);
	}
	// 録画プロセス終了時処理
	recProc.on('exit', finalize);

	// 終了シグナル時処理
	process.on('SIGINT', finalize);
	process.on('SIGQUIT', finalize);
	process.on('SIGTERM', finalize);

	// Tweeter (Experimental)
	if (tweeter && config.operTweeterFormat.start) {
		tweeterUpdater(
			config.operTweeterFormat.start
			.replace('<id>', program.id)
			.replace('<type>', program.channel.type)
			.replace('<channel>', ((program.channel.type === 'CS') ? program.channel.sid : program.channel.channel))
			.replace('<title>',   program.title)
		);
	}
}

// 録画ファイル名
function formatRecordedName(program) {
	var name = config.recordedFormat;

	// <date:?>
	if (name.match(/<date:[^>]+>/) !== null) {
		name = name.replace(/<date:[^>]+>/, dateFormat(new Date(program.start), name.match(/<date:([^>]+)>/)[1]));
	}

	// <id>
	name = name.replace('<id>', program.id);

	// <type>
	name = name.replace('<type>', program.channel.type);

	// <channel>
	name = name.replace('<channel>', (program.channel.type === 'CS') ? program.channel.sid : program.channel.channel);

	// <tuner>
	name = name.replace('<tuner>', program.tuner.name);

	// <title>
	name = name.replace('<title>', program.title);

	// strip
	name = name.replace(/\//g, '／').replace(/\\/g, '＼').replace(/:/g, '：').replace(/\*/g, '＊').replace(/\?/g, '？');
	name = name.replace(/"/g, '”').replace(/</g, '＜').replace(/>/g, '＞').replace(/\|/g, '｜').replace(/≫/g, '＞＞');

	return name;
}
<|endoftext|>


<commit_before><commit_msg>removing extra new line<commit_after><|endoftext|>


<commit_before>"use strict";

class claraConfigurator {
  constructor(config) {
    this.api                    = null;
    this.claraSceneId           = null;
    this.playerDivId            = null;
    this.configuratorDivId      = null;
    this.configuratorInputDivId = null;
    this.configuratorForm       = null;
    this.addtocartClassName     = null;
    this.addtocartButton        = null;
    /*
    *
    */
    this.attributes           = null;
    /*
    *
    */
    this.available_attributes = null;

    if (config) {
      // init variables
      this.playerDivId            = config.playerDivId;
      this.configuratorDivId      = config.configuratorDivId;
      this.claraSceneId           = config.claraSceneId;
      this.attributes             = config.attributes;
      this.available_attributes   = config.available_attributes;
      this.configuratorInputDivId = config.configuratorInputDivId;
      this.addtocartClassName     = config.addtocartClassName;

      this.addtocartButton = document.getElementByClassName(this.addtocartClassName)[0];
    }
    console.log(this.attributes);
    console.log(this.available_attributes);
    this._initClara();
  }

  _initClara() {
    var self = this;
    // Fetch and initialize the sceneId
    this.api = claraplayer(this.playerDivId);
    this.api.sceneIO
      .fetchAndUse(this.claraSceneId, null, { waitForPublish: true })
      .then(() => {

        // use the first form in the scene
        self._getConfiguratorForm();

        var configuratorEl = document.getElementById(self.configuratorDivId);

        if (self.configuratorForm && configuratorEl) {
          self.api.configuration.initConfigurator({
            id    : self.claraSceneId,
            form  : self.configuratorForm,
            el    : configuratorEl
          });

          self.api.on('configurationChange', (ev) => {
            self._onConfigurationChange();
          });
        }
      });
  }

  _getConfiguratorForm() {
    if (!this.api) {
      return;
    }
    var forms = this.api.configuration.getForms();
    if (forms.length > 0) {
      this.configuratorForm = forms[0].name;
    }
  }

  _onConfigurationChange() {
    var self = this;
    var config = this.api.configuration.getConfiguration();
    console.log(config);

    var configuratorInputDiv = document.getElementById(this.configuratorInputDivId);
    if (!configuratorInputDiv) {
      return;
    }

    // check if config attribute name and value exist for the current product
    var additionalAttrs = [];
    var attribute_keys = this.attributes.keys();
    var config_keys = cofig.keys();

    if (cofig_keys.length < attribute_keys.length) {
      console.warn("Threekit attribute number is smaller than in WooCommerce, product will not be able to added to cart");
    }
    for (var key in config) {
      // looking for "key" in attribute_keys
      var found = false;
      var legal = true;
      for (ele in attribute_keys) {
        // remove "pa_"
        var trimEle = ele;
        if (trimEle.startsWith('pa_')) {
          trimEle = trimEle.substr(3);
        }
        if (self.ignoreCaseStrcmp(trimEle, key)) {
          found = true;
          if (!self.attributes[ele].includes(config[key])) {
            // config attribute value is illegal
            legal = false;
          }
          break;
        }
      }
      if (!legal) {
        self._disableAddtocartButton();
        return;
      }
      if (!found) {
        // additional attributes will be posted to server as text
        additionalAttrs.push(key);
      }
    }
    /*
    *  Check if the product is available
    *  Attributes in WooCommerce can be overlapping,
    *  using the first matching attribute in this.available_attributes
    */

    // calculate price

  }

  _disableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartButton.classList.add('disabled');
  }

  _enableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartButton.classList.remove('disabled');
  }

  /*
  * Utility functions
  */
  ignoreCaseStrcmp(str1, str2) {
    return str1.toLowerCase() === str2.toLowerCase();
  }
}

(function() {
  var opts = {
    addtocartClassName    : 'single_add_to_cart_button',
    playerDivId           : 'clara-player',
    configuratorDivId     : 'panel-embed',
    configuratorInputDivId: 'threekit-add-to-cart-inputs',
    claraSceneId          : php_vars.clarauuid,
    available_attributes  : php_vars.available_attributes,
    attributes            : php_vars.attributes
  };
  var cc = new claraConfigurator(opts);

}());
<commit_msg>debug<commit_after>"use strict";

class claraConfigurator {
  constructor(config) {
    this.api                    = null;
    this.claraSceneId           = null;
    this.playerDivId            = null;
    this.configuratorDivId      = null;
    this.configuratorInputDivId = null;
    this.configuratorForm       = null;
    this.addtocartClassName     = null;
    this.addtocartButton        = null;
    /*
    *
    */
    this.attributes           = null;
    /*
    *
    */
    this.available_attributes = null;

    if (config) {
      // init variables
      this.playerDivId            = config.playerDivId;
      this.configuratorDivId      = config.configuratorDivId;
      this.claraSceneId           = config.claraSceneId;
      this.attributes             = config.attributes;
      this.available_attributes   = config.available_attributes;
      this.configuratorInputDivId = config.configuratorInputDivId;
      this.addtocartClassName     = config.addtocartClassName;

      this.addtocartButton = document.getElementsByClassName(this.addtocartClassName)[0];
    }
    console.log(this.attributes);
    console.log(this.available_attributes);
    this._initClara();
  }

  _initClara() {
    var self = this;
    // Fetch and initialize the sceneId
    this.api = claraplayer(this.playerDivId);
    this.api.sceneIO
      .fetchAndUse(this.claraSceneId, null, { waitForPublish: true })
      .then(() => {

        // use the first form in the scene
        self._getConfiguratorForm();

        var configuratorEl = document.getElementById(self.configuratorDivId);

        if (self.configuratorForm && configuratorEl) {
          self.api.configuration.initConfigurator({
            id    : self.claraSceneId,
            form  : self.configuratorForm,
            el    : configuratorEl
          });

          self.api.on('configurationChange', (ev) => {
            self._onConfigurationChange();
          });
        }
      });
  }

  _getConfiguratorForm() {
    if (!this.api) {
      return;
    }
    var forms = this.api.configuration.getForms();
    if (forms.length > 0) {
      this.configuratorForm = forms[0].name;
    }
  }

  _onConfigurationChange() {
    var self = this;
    var config = this.api.configuration.getConfiguration();
    console.log(config);

    var configuratorInputDiv = document.getElementById(this.configuratorInputDivId);
    if (!configuratorInputDiv) {
      return;
    }

    // check if config attribute name and value exist for the current product
    var additionalAttrs = [];
    var attribute_keys = this.attributes.keys();
    var config_keys = cofig.keys();

    if (cofig_keys.length < attribute_keys.length) {
      console.warn("Threekit attribute number is smaller than in WooCommerce, product will not be able to added to cart");
    }
    for (var key in config) {
      // looking for "key" in attribute_keys
      var found = false;
      var legal = true;
      for (ele in attribute_keys) {
        // remove "pa_"
        var trimEle = ele;
        if (trimEle.startsWith('pa_')) {
          trimEle = trimEle.substr(3);
        }
        if (self.ignoreCaseStrcmp(trimEle, key)) {
          found = true;
          if (!self.attributes[ele].includes(config[key])) {
            // config attribute value is illegal
            legal = false;
          }
          break;
        }
      }
      if (!legal) {
        self._disableAddtocartButton();
        return;
      }
      if (!found) {
        // additional attributes will be posted to server as text
        additionalAttrs.push(key);
      }
    }
    /*
    *  Check if the product is available
    *  Attributes in WooCommerce can be overlapping,
    *  using the first matching attribute in this.available_attributes
    */

    // calculate price

  }

  _disableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartButton.classList.add('disabled');
  }

  _enableAddtocartButton() {
    if (!this.addtocartButton) {
      return;
    }
    this.addtocartButton.classList.remove('disabled');
  }

  /*
  * Utility functions
  */
  ignoreCaseStrcmp(str1, str2) {
    return str1.toLowerCase() === str2.toLowerCase();
  }
}

(function() {
  var opts = {
    addtocartClassName    : 'single_add_to_cart_button',
    playerDivId           : 'clara-player',
    configuratorDivId     : 'panel-embed',
    configuratorInputDivId: 'threekit-add-to-cart-inputs',
    claraSceneId          : php_vars.clarauuid,
    available_attributes  : php_vars.available_attributes,
    attributes            : php_vars.attributes
  };
  var cc = new claraConfigurator(opts);

}());
<|endoftext|>


<commit_before><commit_msg>thought there was no way that small change could break anything. Should have  looked at the js tests before checking in.<commit_after><|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var ForeignFunction = require('./foreign_function')
  , assert = require('assert')
  , debug = require('debug')('ffi:DynamicLibrary')
  , bindings = require('./bindings')
  , funcs = bindings.StaticFunctions
  , ref = require('ref')
  , read  = require('fs').readFileSync

// typedefs
var int = ref.types.int
  , voidPtr = ref.refType(ref.types.void)

var dlopen  = ForeignFunction(funcs.dlopen,  voidPtr, [ 'string', int ])
  , dlclose = ForeignFunction(funcs.dlclose, int,     [ voidPtr ])
  , dlsym   = ForeignFunction(funcs.dlsym,   voidPtr, [ voidPtr, 'string' ])
  , dlerror = ForeignFunction(funcs.dlerror, 'string', [ ])

/**
 * `DynamicLibrary` loads and fetches function pointers for dynamic libraries
 * (.so, .dylib, etc). After the libray's function pointer is acquired, then you
 * call `get(symbol)` to retreive a pointer to an exported symbol. You need to
 * call `get___()` on the pointer to dereference it into it's acutal value, or
 * turn the pointer into a callable function with `ForeignFunction`.
 */

function DynamicLibrary (path, mode) {
  if (!(this instanceof DynamicLibrary)) {
    return new DynamicLibrary(path, mode)
  }
  debug('new DynamicLibrary()', path, mode)

  if (null == mode) {
    mode = DynamicLibrary.FLAGS.RTLD_LAZY
  }

  this._handle = dlopen(path, mode)
  assert(Buffer.isBuffer(this._handle), 'expected a Buffer instance to be returned from `dlopen()`')

  if (this._handle.isNull()) {
    var err = this.error()

    // THIS CODE IS BASED ON GHC Trac ticket #2615
    // http://hackage.haskell.org/trac/ghc/attachment/ticket/2615

    // On some systems (e.g., Gentoo Linux) dynamic files (e.g. libc.so)
    // contain linker scripts rather than ELF-format object code. This
    // code handles the situation by recognizing the real object code
    // file name given in the linker script.

    // If an "invalid ELF header" error occurs, it is assumed that the
    // .so file contains a linker script instead of ELF object code.
    // In this case, the code looks for the GROUP ( ... ) linker
    // directive. If one is found, the first file name inside the
    // parentheses is treated as the name of a dynamic library and the
    // code attempts to dlopen that file. If this is also unsuccessful,
    // an error message is returned.

    // see if the error message is due to an invalid ELF header
    var match

    if (match = err.match(/^(([^ \t()])+\.so([^ \t:()])*):([ \t])*invalid ELF header$/)) {
      var content = read(match[1], 'ascii')
      // try to find a GROUP ( ... ) command
      if (match = content.match(/GROUP *\( *(([^ )])+)/)){
        return DynamicLibrary.call(this, match[1], mode)
      }
    }

    throw new Error('Dynamic Linking Error: ' + err)
  }
}
module.exports = DynamicLibrary

/**
 * Set the exported flags from "dlfcn.h"
 */

DynamicLibrary.FLAGS = {};
Object.keys(bindings).forEach(function (k) {
  if (!/^RTLD_/.test(k)) return;
  DynamicLibrary.FLAGS[k] = bindings[k];
});

/**
 * Close this library, returns the result of the dlclose() system function.
 */

DynamicLibrary.prototype.close = function () {
  debug('dlclose()')
  return dlclose(this._handle)
}

/**
 * Get a symbol from this library, returns a Pointer for (memory address of) the symbol
 */

DynamicLibrary.prototype.get = function (symbol) {
  debug('dlsym()', symbol)
  assert.equal('string', typeof symbol)

  var ptr = dlsym(this._handle, symbol)
  assert(Buffer.isBuffer(ptr))

  if (ptr.isNull()) {
    throw new Error('Dynamic Symbol Retrieval Error: ' + this.error())
  }

  ptr.name = symbol

  return ptr
}

/**
 * Returns the result of the dlerror() system function
 */

DynamicLibrary.prototype.error = function error () {
  debug('dlerror()')
  return dlerror()
}
<commit_msg>dynamic_library: inherit the descriptors for the FLAGS<commit_after>
/**
 * Module dependencies.
 */

var ForeignFunction = require('./foreign_function')
  , assert = require('assert')
  , debug = require('debug')('ffi:DynamicLibrary')
  , bindings = require('./bindings')
  , funcs = bindings.StaticFunctions
  , ref = require('ref')
  , read  = require('fs').readFileSync

// typedefs
var int = ref.types.int
  , voidPtr = ref.refType(ref.types.void)

var dlopen  = ForeignFunction(funcs.dlopen,  voidPtr, [ 'string', int ])
  , dlclose = ForeignFunction(funcs.dlclose, int,     [ voidPtr ])
  , dlsym   = ForeignFunction(funcs.dlsym,   voidPtr, [ voidPtr, 'string' ])
  , dlerror = ForeignFunction(funcs.dlerror, 'string', [ ])

/**
 * `DynamicLibrary` loads and fetches function pointers for dynamic libraries
 * (.so, .dylib, etc). After the libray's function pointer is acquired, then you
 * call `get(symbol)` to retreive a pointer to an exported symbol. You need to
 * call `get___()` on the pointer to dereference it into it's acutal value, or
 * turn the pointer into a callable function with `ForeignFunction`.
 */

function DynamicLibrary (path, mode) {
  if (!(this instanceof DynamicLibrary)) {
    return new DynamicLibrary(path, mode)
  }
  debug('new DynamicLibrary()', path, mode)

  if (null == mode) {
    mode = DynamicLibrary.FLAGS.RTLD_LAZY
  }

  this._handle = dlopen(path, mode)
  assert(Buffer.isBuffer(this._handle), 'expected a Buffer instance to be returned from `dlopen()`')

  if (this._handle.isNull()) {
    var err = this.error()

    // THIS CODE IS BASED ON GHC Trac ticket #2615
    // http://hackage.haskell.org/trac/ghc/attachment/ticket/2615

    // On some systems (e.g., Gentoo Linux) dynamic files (e.g. libc.so)
    // contain linker scripts rather than ELF-format object code. This
    // code handles the situation by recognizing the real object code
    // file name given in the linker script.

    // If an "invalid ELF header" error occurs, it is assumed that the
    // .so file contains a linker script instead of ELF object code.
    // In this case, the code looks for the GROUP ( ... ) linker
    // directive. If one is found, the first file name inside the
    // parentheses is treated as the name of a dynamic library and the
    // code attempts to dlopen that file. If this is also unsuccessful,
    // an error message is returned.

    // see if the error message is due to an invalid ELF header
    var match

    if (match = err.match(/^(([^ \t()])+\.so([^ \t:()])*):([ \t])*invalid ELF header$/)) {
      var content = read(match[1], 'ascii')
      // try to find a GROUP ( ... ) command
      if (match = content.match(/GROUP *\( *(([^ )])+)/)){
        return DynamicLibrary.call(this, match[1], mode)
      }
    }

    throw new Error('Dynamic Linking Error: ' + err)
  }
}
module.exports = DynamicLibrary

/**
 * Set the exported flags from "dlfcn.h"
 */

DynamicLibrary.FLAGS = {};
Object.keys(bindings).forEach(function (k) {
  if (!/^RTLD_/.test(k)) return;
  var desc = Object.getOwnPropertyDescriptor(bindings, k)
  Object.defineProperty(DynamicLibrary.FLAGS, k, desc)
});

/**
 * Close this library, returns the result of the dlclose() system function.
 */

DynamicLibrary.prototype.close = function () {
  debug('dlclose()')
  return dlclose(this._handle)
}

/**
 * Get a symbol from this library, returns a Pointer for (memory address of) the symbol
 */

DynamicLibrary.prototype.get = function (symbol) {
  debug('dlsym()', symbol)
  assert.equal('string', typeof symbol)

  var ptr = dlsym(this._handle, symbol)
  assert(Buffer.isBuffer(ptr))

  if (ptr.isNull()) {
    throw new Error('Dynamic Symbol Retrieval Error: ' + this.error())
  }

  ptr.name = symbol

  return ptr
}

/**
 * Returns the result of the dlerror() system function
 */

DynamicLibrary.prototype.error = function error () {
  debug('dlerror()')
  return dlerror()
}
<|endoftext|>


<commit_before><commit_msg>Delete progress.all.min.js<commit_after><|endoftext|>


<commit_before><commit_msg>Return extended class<commit_after><|endoftext|>