<commit_before>beforeEach(function(){
  compileCache = {};
});

describe('Angular', function(){
  xit('should fire on updateEvents', function(){
    var onUpdateView = jasmine.createSpy();
    var scope = angular.compile("

", { onUpdateView: onUpdateView });
    expect(onUpdateView).wasNotCalled();
    scope.$init();
    scope.$eval();
    expect(onUpdateView).wasCalled();
  });
});

describe("copy", function(){
  it("should return same object", function (){
    var obj = {};
    var arr = [];
    assertSame(obj, copy({}, obj));
    assertSame(arr, copy([], arr));
  });

  it("should copy Date", function(){
    var date = new Date(123);
    expect(copy(date) instanceof Date).toBeTruthy();
    expect(copy(date).getTime()).toEqual(123);
    expect(copy(date) === date).toBeFalsy();
  });

  it("should copy array", function(){
    var src = [1, {name:"value"}];
    var dst = [{key:"v"}];
    assertSame(dst, copy(src, dst));
    assertEquals([1, {name:"value"}], dst);
    assertEquals({name:"value"}, dst[1]);
    assertNotSame(src[1], dst[1]);
  });

  it('should copy empty array', function() {
    var src = [];
    var dst = [{key: "v"}];
    assertEquals([], copy(src, dst));
    assertEquals([], dst);
  });

  it("should copy object", function(){
    var src = {a:{name:"value"}};
    var dst = {b:{key:"v"}};
    assertSame(dst, copy(src, dst));
    assertEquals({a:{name:"value"}}, dst);
    assertEquals(src.a, dst.a);
    assertNotSame(src.a, dst.a);
  });

  it("should copy primitives", function(){
    expect(copy(null)).toEqual(null);
    expect(copy('')).toEqual('');
    expect(copy(123)).toEqual(123);
    expect(copy([{key:null}])).toEqual([{key:null}]);
  });

});

describe('equals', function(){
  it('should return true if same object', function(){
    var o = {};
    expect(equals(o, o)).toEqual(true);
    expect(equals(1, '1')).toEqual(true);
    expect(equals(1, '2')).toEqual(false);
  });

  it('should recurse into object', function(){
    expect(equals({}, {})).toEqual(true);
    expect(equals({name:'misko'}, {name:'misko'})).toEqual(true);
    expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false);
    expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false);
    expect(equals({name:'misko'}, {name:'adam'})).toEqual(false);
    expect(equals(['misko'], ['misko'])).toEqual(true);
    expect(equals(['misko'], ['adam'])).toEqual(false);
    expect(equals(['misko'], ['misko', 'adam'])).toEqual(false);
  });

  it('should ignore $ member variables', function(){
    expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true);
    expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true);
    expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true);
  });

  it('should ignore functions', function(){
    expect(equals({func: function() {}}, {bar: function() {}})).toEqual(true);
  });
});

describe('parseKeyValue', function() {
  it('should parse a string into key-value pairs', function() {
    expect(parseKeyValue('')).toEqual({});
    expect(parseKeyValue('simple=pair')).toEqual({simple: 'pair'});
    expect(parseKeyValue('first=1&second=2')).toEqual({first: '1', second: '2'});
    expect(parseKeyValue('escaped%20key=escaped%20value')).
      toEqual({'escaped key': 'escaped value'});
    expect(parseKeyValue('emptyKey=')).toEqual({emptyKey: ''});
    expect(parseKeyValue('flag1&key=value&flag2')).
      toEqual({flag1: true, key: 'value', flag2: true});
  });
});
<commit_msg>Add missing tests for toKeyValue() function<commit_after>beforeEach(function(){
  compileCache = {};
});

describe('Angular', function(){
  xit('should fire on updateEvents', function(){
    var onUpdateView = jasmine.createSpy();
    var scope = angular.compile("

", { onUpdateView: onUpdateView });
    expect(onUpdateView).wasNotCalled();
    scope.$init();
    scope.$eval();
    expect(onUpdateView).wasCalled();
  });
});

describe("copy", function(){
  it("should return same object", function (){
    var obj = {};
    var arr = [];
    assertSame(obj, copy({}, obj));
    assertSame(arr, copy([], arr));
  });

  it("should copy Date", function(){
    var date = new Date(123);
    expect(copy(date) instanceof Date).toBeTruthy();
    expect(copy(date).getTime()).toEqual(123);
    expect(copy(date) === date).toBeFalsy();
  });

  it("should copy array", function(){
    var src = [1, {name:"value"}];
    var dst = [{key:"v"}];
    assertSame(dst, copy(src, dst));
    assertEquals([1, {name:"value"}], dst);
    assertEquals({name:"value"}, dst[1]);
    assertNotSame(src[1], dst[1]);
  });

  it('should copy empty array', function() {
    var src = [];
    var dst = [{key: "v"}];
    assertEquals([], copy(src, dst));
    assertEquals([], dst);
  });

  it("should copy object", function(){
    var src = {a:{name:"value"}};
    var dst = {b:{key:"v"}};
    assertSame(dst, copy(src, dst));
    assertEquals({a:{name:"value"}}, dst);
    assertEquals(src.a, dst.a);
    assertNotSame(src.a, dst.a);
  });

  it("should copy primitives", function(){
    expect(copy(null)).toEqual(null);
    expect(copy('')).toEqual('');
    expect(copy(123)).toEqual(123);
    expect(copy([{key:null}])).toEqual([{key:null}]);
  });

});

describe('equals', function(){
  it('should return true if same object', function(){
    var o = {};
    expect(equals(o, o)).toEqual(true);
    expect(equals(1, '1')).toEqual(true);
    expect(equals(1, '2')).toEqual(false);
  });

  it('should recurse into object', function(){
    expect(equals({}, {})).toEqual(true);
    expect(equals({name:'misko'}, {name:'misko'})).toEqual(true);
    expect(equals({name:'misko', age:1}, {name:'misko'})).toEqual(false);
    expect(equals({name:'misko'}, {name:'misko', age:1})).toEqual(false);
    expect(equals({name:'misko'}, {name:'adam'})).toEqual(false);
    expect(equals(['misko'], ['misko'])).toEqual(true);
    expect(equals(['misko'], ['adam'])).toEqual(false);
    expect(equals(['misko'], ['misko', 'adam'])).toEqual(false);
  });

  it('should ignore $ member variables', function(){
    expect(equals({name:'misko', $id:1}, {name:'misko', $id:2})).toEqual(true);
    expect(equals({name:'misko'}, {name:'misko', $id:2})).toEqual(true);
    expect(equals({name:'misko', $id:1}, {name:'misko'})).toEqual(true);
  });

  it('should ignore functions', function(){
    expect(equals({func: function() {}}, {bar: function() {}})).toEqual(true);
  });
});

describe('parseKeyValue', function() {
  it('should parse a string into key-value pairs', function() {
    expect(parseKeyValue('')).toEqual({});
    expect(parseKeyValue('simple=pair')).toEqual({simple: 'pair'});
    expect(parseKeyValue('first=1&second=2')).toEqual({first: '1', second: '2'});
    expect(parseKeyValue('escaped%20key=escaped%20value')).
      toEqual({'escaped key': 'escaped value'});
    expect(parseKeyValue('emptyKey=')).toEqual({emptyKey: ''});
    expect(parseKeyValue('flag1&key=value&flag2')).
      toEqual({flag1: true, key: 'value', flag2: true});
  });
});

describe('toKeyValue', function() {
  it('should parse key-value pairs into string', function() {
    expect(toKeyValue({})).toEqual('');
    expect(toKeyValue({simple: 'pair'})).toEqual('simple=pair');
    expect(toKeyValue({first: '1', second: '2'})).toEqual('first=1&second=2');
    expect(toKeyValue({'escaped key': 'escaped value'})).
      toEqual('escaped%20key=escaped%20value');
    expect(toKeyValue({emptyKey: ''})).toEqual('emptyKey=');
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import {
    Animated,
    Keyboard,
    SafeAreaView,
    TextInput,
    TouchableHighlight,
    TouchableOpacity,
    View
} from 'react-native';

import { getName } from '../../app/functions';
import { ColorSchemeRegistry } from '../../base/color-scheme';
import { translate } from '../../base/i18n';
import { Icon, IconMenu, IconWarning } from '../../base/icons';
import { MEDIA_TYPE } from '../../base/media';
import { Header, LoadingIndicator, Text } from '../../base/react';
import { connect } from '../../base/redux';
import { ColorPalette } from '../../base/styles';
import {
    createDesiredLocalTracks,
    destroyLocalDesktopTrackIfExists,
    destroyLocalTracks
} from '../../base/tracks';
import { HelpView } from '../../help';
import { DialInSummary } from '../../invite';
import { SettingsView } from '../../settings';
import { setSideBarVisible } from '../actions';

import {
    AbstractWelcomePage,
    _mapStateToProps as _abstractMapStateToProps
} from './AbstractWelcomePage';
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
import VideoSwitch from './VideoSwitch';
import WelcomePageLists from './WelcomePageLists';
import WelcomePageSideBar from './WelcomePageSideBar';
import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';

/**
 * The native container rendering the welcome page.
 *
 * @extends AbstractWelcomePage
 */
class WelcomePage extends AbstractWelcomePage {
    /**
     * Constructor of the Component.
     *
     * @inheritdoc
     */
    constructor(props) {
        super(props);

        this.state._fieldFocused = false;
        this.state.hintBoxAnimation = new Animated.Value(0);

        // Bind event handlers so they are only bound once per instance.
        this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
        this._onShowSideBar = this._onShowSideBar.bind(this);
        this._renderHintBox = this._renderHintBox.bind(this);

        // Specially bind functions to avoid function definition on render.
        this._onFieldBlur = this._onFieldFocusChange.bind(this, false);
        this._onFieldFocus = this._onFieldFocusChange.bind(this, true);
    }

    /**
     * Implements React's {@link Component#componentDidMount()}. Invoked
     * immediately after mounting occurs. Creates a local video track if none
     * is available and the camera permission was already granted.
     *
     * @inheritdoc
     * @returns {void}
     */
    componentDidMount() {
        super.componentDidMount();

        this._updateRoomname();

        const { dispatch } = this.props;

        if (this.props._settings.startAudioOnly) {
            dispatch(destroyLocalTracks());
        } else {
            dispatch(destroyLocalDesktopTrackIfExists());

            // Make sure we don't request the permission for the camera from
            // the start. We will, however, create a video track iff the user
            // already granted the permission.
            navigator.permissions.query({ name: 'camera' }).then(response => {
                response === 'granted'
                    && dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));
            });
        }
    }

    /**
     * Implements React's {@link Component#render()}. Renders a prompt for
     * entering a room name.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        // We want to have the welcome page support the reduced UI layout,
        // but we ran into serious issues enabling it so we disable it
        // until we have a proper fix in place. We leave the code here though, because
        // this part should be fine when the bug is fixed.
        //
        // NOTE: when re-enabling, don't forget to uncomment the respective _mapStateToProps line too

        /*
        const { _reducedUI } = this.props;

        if (_reducedUI) {
            return this._renderReducedUI();
        }
        */

        return this._renderFullUI();
    }

    /**
     * Renders the insecure room name warning.
     *
     * @inheritdoc
     */
    _doRenderInsecureRoomNameWarning() {
        return (
            <View
                style = { [
                    styles.messageContainer,
                    styles.insecureRoomNameWarningContainer
                ] }>
                <Icon
                    src = { IconWarning }
                    style = { styles.insecureRoomNameWarningIcon } />
                <Text style = { styles.insecureRoomNameWarningText }>
                    { this.props.t('security.insecureRoomNameWarning') }
                </Text>
            </View>
        );
    }

    /**
     * Constructs a style array to handle the hint box animation.
     *
     * @private
     * @returns {Array<Object>}
     */
    _getHintBoxStyle() {
        return [
            styles.messageContainer,
            styles.hintContainer,
            {
                opacity: this.state.hintBoxAnimation
            }
        ];
    }

    /**
     * Callback for when the room field's focus changes so the hint box
     * must be rendered or removed.
     *
     * @private
     * @param {boolean} focused - The focused state of the field.
     * @returns {void}
     */
    _onFieldFocusChange(focused) {
        focused
            && this.setState({
                _fieldFocused: true
            });

        Animated.timing(
            this.state.hintBoxAnimation,
            {
                duration: 300,
                toValue: focused ? 1 : 0
            })
            .start(animationState =>
                animationState.finished
                    && !focused
                    && this.setState({
                        _fieldFocused: false
                    }));
    }

    /**
     * Toggles the side bar.
     *
     * @private
     * @returns {void}
     */
    _onShowSideBar() {
        Keyboard.dismiss();
        this.props.dispatch(setSideBarVisible(true));
    }

    /**
     * Renders the hint box if necessary.
     *
     * @private
     * @returns {React$Node}
     */
    _renderHintBox() {
        if (this.state._fieldFocused) {
            const { t } = this.props;

            return (
                <Animated.View style = { this._getHintBoxStyle() }>
                    <View style = { styles.hintTextContainer } >
                        <Text style = { styles.hintText }>
                            { t('welcomepage.roomnameHint') }
                        </Text>
                    </View>
                    <View style = { styles.hintButtonContainer } >
                        { this._renderJoinButton() }
                    </View>
                </Animated.View>
            );
        }

        return null;
    }

    /**
     * Renders the join button.
     *
     * @private
     * @returns {ReactElement}
     */
    _renderJoinButton() {
        const { t } = this.props;
        let children;

        if (this.state.joining) {
            // TouchableHighlight is picky about what its children can be, so
            // wrap it in a native component, i.e. View to avoid having to
            // modify non-native children.
            children = (
                <View>
                    <LoadingIndicator
                        color = { styles.buttonText.color }
                        size = 'small' />
                </View>
            );
        } else {
            children = (
                <Text style = { styles.buttonText }>
                    { this.props.t('welcomepage.join') }
                </Text>
            );
        }

        return (
            <TouchableHighlight
                accessibilityLabel =
                    { t('welcomepage.accessibilityLabel.join') }
                onPress = { this._onJoin }
                style = { styles.button }
                underlayColor = { ColorPalette.white }>
                { children }
            </TouchableHighlight>
        );
    }

    /**
     * Renders the full welcome page.
     *
     * @returns {ReactElement}
     */
    _renderFullUI() {
        const roomnameAccLabel = 'welcomepage.accessibilityLabel.roomname';
        const { _headerStyles, t } = this.props;

        return (
            <LocalVideoTrackUnderlay style = { styles.welcomePage }>
                <View style = { _headerStyles.page }>
                    <Header style = { styles.header }>
                        <TouchableOpacity onPress = { this._onShowSideBar } >
                            <Icon
                                src = { IconMenu }
                                style = { _headerStyles.headerButtonIcon } />
                        </TouchableOpacity>
                        <VideoSwitch />
                    </Header>
                    <SafeAreaView style = { styles.roomContainer } >
                        <View style = { styles.joinControls } >
                            <Text style = { styles.enterRoomText }>
                                { t('welcomepage.roomname') }
                            </Text>
                            <TextInput
                                accessibilityLabel = { t(roomnameAccLabel) }
                                autoCapitalize = 'none'
                                autoComplete = 'off'
                                autoCorrect = { false }
                                autoFocus = { false }
                                onBlur = { this._onFieldBlur }
                                onChangeText = { this._onRoomChange }
                                onFocus = { this._onFieldFocus }
                                onSubmitEditing = { this._onJoin }
                                placeholder = { this.state.roomPlaceholder }
                                placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
                                returnKeyType = { 'go' }
                                style = { styles.textInput }
                                underlineColorAndroid = 'transparent'
                                value = { this.state.room } />
                            {
                                this._renderInsecureRoomNameWarning()
                            }
                            {
                                this._renderHintBox()
                            }
                        </View>
                    </SafeAreaView>
                    <WelcomePageLists disabled = { this.state._fieldFocused } />
                </View>
                <WelcomePageSideBar />
                { this._renderWelcomePageModals() }
            </LocalVideoTrackUnderlay>
        );
    }

    /**
     * Renders a "reduced" version of the welcome page.
     *
     * @returns {ReactElement}
     */
    _renderReducedUI() {
        const { t } = this.props;

        return (
            <View style = { styles.reducedUIContainer }>
                <Text style = { styles.reducedUIText }>
                    { t('welcomepage.reducedUIText', { app: getName() }) }
                </Text>
            </View>
        );
    }

    /**
     * Renders JitsiModals that are supposed to be on the welcome page.
     *
     * @returns {Array<ReactElement>}
     */
    _renderWelcomePageModals() {
        return [
            <HelpView key = 'helpView' />,
            <DialInSummary key = 'dialInSummary' />,
            <SettingsView key = 'settings' />
        ];
    }
}

/**
 * Maps part of the Redux state to the props of this component.
 *
 * @param {Object} state - The Redux state.
 * @returns {Object}
 */
function _mapStateToProps(state) {
    return {
        ..._abstractMapStateToProps(state),
        _headerStyles: ColorSchemeRegistry.get(state, 'Header')

        // _reducedUI: state['features/base/responsive-ui'].reducedUI
    };
}

export default translate(connect(_mapStateToProps)(WelcomePage));
<commit_msg>fix(rn) stop room name generator when field is focused<commit_after>import React from 'react';
import {
    Animated,
    Keyboard,
    SafeAreaView,
    TextInput,
    TouchableHighlight,
    TouchableOpacity,
    View
} from 'react-native';

import { getName } from '../../app/functions';
import { ColorSchemeRegistry } from '../../base/color-scheme';
import { translate } from '../../base/i18n';
import { Icon, IconMenu, IconWarning } from '../../base/icons';
import { MEDIA_TYPE } from '../../base/media';
import { Header, LoadingIndicator, Text } from '../../base/react';
import { connect } from '../../base/redux';
import { ColorPalette } from '../../base/styles';
import {
    createDesiredLocalTracks,
    destroyLocalDesktopTrackIfExists,
    destroyLocalTracks
} from '../../base/tracks';
import { HelpView } from '../../help';
import { DialInSummary } from '../../invite';
import { SettingsView } from '../../settings';
import { setSideBarVisible } from '../actions';

import {
    AbstractWelcomePage,
    _mapStateToProps as _abstractMapStateToProps
} from './AbstractWelcomePage';
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
import VideoSwitch from './VideoSwitch';
import WelcomePageLists from './WelcomePageLists';
import WelcomePageSideBar from './WelcomePageSideBar';
import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';

/**
 * The native container rendering the welcome page.
 *
 * @extends AbstractWelcomePage
 */
class WelcomePage extends AbstractWelcomePage {
    /**
     * Constructor of the Component.
     *
     * @inheritdoc
     */
    constructor(props) {
        super(props);

        this.state._fieldFocused = false;
        this.state.hintBoxAnimation = new Animated.Value(0);

        // Bind event handlers so they are only bound once per instance.
        this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
        this._onShowSideBar = this._onShowSideBar.bind(this);
        this._renderHintBox = this._renderHintBox.bind(this);

        // Specially bind functions to avoid function definition on render.
        this._onFieldBlur = this._onFieldFocusChange.bind(this, false);
        this._onFieldFocus = this._onFieldFocusChange.bind(this, true);
    }

    /**
     * Implements React's {@link Component#componentDidMount()}. Invoked
     * immediately after mounting occurs. Creates a local video track if none
     * is available and the camera permission was already granted.
     *
     * @inheritdoc
     * @returns {void}
     */
    componentDidMount() {
        super.componentDidMount();

        this._updateRoomname();

        const { dispatch } = this.props;

        if (this.props._settings.startAudioOnly) {
            dispatch(destroyLocalTracks());
        } else {
            dispatch(destroyLocalDesktopTrackIfExists());

            // Make sure we don't request the permission for the camera from
            // the start. We will, however, create a video track iff the user
            // already granted the permission.
            navigator.permissions.query({ name: 'camera' }).then(response => {
                response === 'granted'
                    && dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));
            });
        }
    }

    /**
     * Implements React's {@link Component#render()}. Renders a prompt for
     * entering a room name.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        // We want to have the welcome page support the reduced UI layout,
        // but we ran into serious issues enabling it so we disable it
        // until we have a proper fix in place. We leave the code here though, because
        // this part should be fine when the bug is fixed.
        //
        // NOTE: when re-enabling, don't forget to uncomment the respective _mapStateToProps line too

        /*
        const { _reducedUI } = this.props;

        if (_reducedUI) {
            return this._renderReducedUI();
        }
        */

        return this._renderFullUI();
    }

    /**
     * Renders the insecure room name warning.
     *
     * @inheritdoc
     */
    _doRenderInsecureRoomNameWarning() {
        return (
            <View
                style = { [
                    styles.messageContainer,
                    styles.insecureRoomNameWarningContainer
                ] }>
                <Icon
                    src = { IconWarning }
                    style = { styles.insecureRoomNameWarningIcon } />
                <Text style = { styles.insecureRoomNameWarningText }>
                    { this.props.t('security.insecureRoomNameWarning') }
                </Text>
            </View>
        );
    }

    /**
     * Constructs a style array to handle the hint box animation.
     *
     * @private
     * @returns {Array<Object>}
     */
    _getHintBoxStyle() {
        return [
            styles.messageContainer,
            styles.hintContainer,
            {
                opacity: this.state.hintBoxAnimation
            }
        ];
    }

    /**
     * Callback for when the room field's focus changes so the hint box
     * must be rendered or removed.
     *
     * @private
     * @param {boolean} focused - The focused state of the field.
     * @returns {void}
     */
    _onFieldFocusChange(focused) {
        if (focused) {
            // Stop placeholder animation.
            this._clearTimeouts();
            this.setState({
                _fieldFocused: true,
                roomPlaceholder: ''
            });
        } else {
            // Restart room placeholder animation.
            this._updateRoomname();
        }

        Animated.timing(
            this.state.hintBoxAnimation,
            {
                duration: 300,
                toValue: focused ? 1 : 0
            })
            .start(animationState =>
                animationState.finished
                    && !focused
                    && this.setState({
                        _fieldFocused: false
                    }));
    }

    /**
     * Toggles the side bar.
     *
     * @private
     * @returns {void}
     */
    _onShowSideBar() {
        Keyboard.dismiss();
        this.props.dispatch(setSideBarVisible(true));
    }

    /**
     * Renders the hint box if necessary.
     *
     * @private
     * @returns {React$Node}
     */
    _renderHintBox() {
        if (this.state._fieldFocused) {
            const { t } = this.props;

            return (
                <Animated.View style = { this._getHintBoxStyle() }>
                    <View style = { styles.hintTextContainer } >
                        <Text style = { styles.hintText }>
                            { t('welcomepage.roomnameHint') }
                        </Text>
                    </View>
                    <View style = { styles.hintButtonContainer } >
                        { this._renderJoinButton() }
                    </View>
                </Animated.View>
            );
        }

        return null;
    }

    /**
     * Renders the join button.
     *
     * @private
     * @returns {ReactElement}
     */
    _renderJoinButton() {
        const { t } = this.props;
        let children;

        if (this.state.joining) {
            // TouchableHighlight is picky about what its children can be, so
            // wrap it in a native component, i.e. View to avoid having to
            // modify non-native children.
            children = (
                <View>
                    <LoadingIndicator
                        color = { styles.buttonText.color }
                        size = 'small' />
                </View>
            );
        } else {
            children = (
                <Text style = { styles.buttonText }>
                    { this.props.t('welcomepage.join') }
                </Text>
            );
        }

        return (
            <TouchableHighlight
                accessibilityLabel =
                    { t('welcomepage.accessibilityLabel.join') }
                onPress = { this._onJoin }
                style = { styles.button }
                underlayColor = { ColorPalette.white }>
                { children }
            </TouchableHighlight>
        );
    }

    /**
     * Renders the full welcome page.
     *
     * @returns {ReactElement}
     */
    _renderFullUI() {
        const roomnameAccLabel = 'welcomepage.accessibilityLabel.roomname';
        const { _headerStyles, t } = this.props;

        return (
            <LocalVideoTrackUnderlay style = { styles.welcomePage }>
                <View style = { _headerStyles.page }>
                    <Header style = { styles.header }>
                        <TouchableOpacity onPress = { this._onShowSideBar } >
                            <Icon
                                src = { IconMenu }
                                style = { _headerStyles.headerButtonIcon } />
                        </TouchableOpacity>
                        <VideoSwitch />
                    </Header>
                    <SafeAreaView style = { styles.roomContainer } >
                        <View style = { styles.joinControls } >
                            <Text style = { styles.enterRoomText }>
                                { t('welcomepage.roomname') }
                            </Text>
                            <TextInput
                                accessibilityLabel = { t(roomnameAccLabel) }
                                autoCapitalize = 'none'
                                autoComplete = 'off'
                                autoCorrect = { false }
                                autoFocus = { false }
                                onBlur = { this._onFieldBlur }
                                onChangeText = { this._onRoomChange }
                                onFocus = { this._onFieldFocus }
                                onSubmitEditing = { this._onJoin }
                                placeholder = { this.state.roomPlaceholder }
                                placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
                                returnKeyType = { 'go' }
                                style = { styles.textInput }
                                underlineColorAndroid = 'transparent'
                                value = { this.state.room } />
                            {
                                this._renderInsecureRoomNameWarning()
                            }
                            {
                                this._renderHintBox()
                            }
                        </View>
                    </SafeAreaView>
                    <WelcomePageLists disabled = { this.state._fieldFocused } />
                </View>
                <WelcomePageSideBar />
                { this._renderWelcomePageModals() }
            </LocalVideoTrackUnderlay>
        );
    }

    /**
     * Renders a "reduced" version of the welcome page.
     *
     * @returns {ReactElement}
     */
    _renderReducedUI() {
        const { t } = this.props;

        return (
            <View style = { styles.reducedUIContainer }>
                <Text style = { styles.reducedUIText }>
                    { t('welcomepage.reducedUIText', { app: getName() }) }
                </Text>
            </View>
        );
    }

    /**
     * Renders JitsiModals that are supposed to be on the welcome page.
     *
     * @returns {Array<ReactElement>}
     */
    _renderWelcomePageModals() {
        return [
            <HelpView key = 'helpView' />,
            <DialInSummary key = 'dialInSummary' />,
            <SettingsView key = 'settings' />
        ];
    }
}

/**
 * Maps part of the Redux state to the props of this component.
 *
 * @param {Object} state - The Redux state.
 * @returns {Object}
 */
function _mapStateToProps(state) {
    return {
        ..._abstractMapStateToProps(state),
        _headerStyles: ColorSchemeRegistry.get(state, 'Header')

        // _reducedUI: state['features/base/responsive-ui'].reducedUI
    };
}

export default translate(connect(_mapStateToProps)(WelcomePage));
<|endoftext|>


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

module.exports = {
  queryDefaults: {
    size: 0
  },
  defaultAggregates: {
    facets: {
      global: {},
      aggs: {}
    },
    byWork: {
      terms: {
        field: 'workUri',
        order: { top: 'desc' },
        size: Constants.maxSearchResults
      },
      aggs: {
        publications: {
          top_hits: {
            size: 1
          }
        },
        top: {
          max: {
            script: {
              lang: 'painless',
              // values to tweak:
              //   0.5 = gain (weight factor)
              //   100 = scale (in days since catalogued)
              inline: "if (doc.created.value != null) { return _score * (1 + (0.5*100)/(100+(System.currentTimeMillis()-doc.created.date.getMillis())/86400000)); } return _score;"
            }
          }
        }
      }
    },
    workCount: {
      cardinality: {
        field: 'workUri'
      }
    }
  },
  defaultFields: [
    'agents',
    'author^50',
    'bio',
    'compType',
    'country',
    'desc',
    'ean',
    'format',
    'genre',
    'inst',
    'isbn',
    'ismn',
    'language',
    'litform',
    'mainTitle^30',
    'mt',
    'partNumber',
    'partTitle',
    'publishedBy',
    'recordId',
    'series^10',
    'subject^10',
    'summary',
    'title^20',
    'workMainTitle',
    'workPartNumber',
    'workPartTitle',
    'workSubtitle'
  ]
}
<commit_msg>patron-client: linting<commit_after>const Constants = require('../../frontend/constants/Constants')

module.exports = {
  queryDefaults: {
    size: 0
  },
  defaultAggregates: {
    facets: {
      global: {},
      aggs: {}
    },
    byWork: {
      terms: {
        field: 'workUri',
        order: { top: 'desc' },
        size: Constants.maxSearchResults
      },
      aggs: {
        publications: {
          top_hits: {
            size: 1
          }
        },
        top: {
          max: {
            script: {
              lang: 'painless',
              // values to tweak:
              //   0.5 = gain (weight factor)
              //   100 = scale (in days since catalogued)
              inline: 'if (doc.created.value != null) { return _score * (1 + (0.5*100)/(100+(System.currentTimeMillis()-doc.created.date.getMillis())/86400000)); } return _score;'
            }
          }
        }
      }
    },
    workCount: {
      cardinality: {
        field: 'workUri'
      }
    }
  },
  defaultFields: [
    'agents',
    'author^50',
    'bio',
    'compType',
    'country',
    'desc',
    'ean',
    'format',
    'genre',
    'inst',
    'isbn',
    'ismn',
    'language',
    'litform',
    'mainTitle^30',
    'mt',
    'partNumber',
    'partTitle',
    'publishedBy',
    'recordId',
    'series^10',
    'subject^10',
    'summary',
    'title^20',
    'workMainTitle',
    'workPartNumber',
    'workPartTitle',
    'workSubtitle'
  ]
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import Component from '@glimmer/component';
import { task, timeout } from 'ember-concurrency';
import { assert } from '@ember/debug';
import { inject as service } from '@ember/service';
import { action, get } from '@ember/object';

export default class NodePrimaryMetric extends Component {
  @service token;
  @service('stats-trackers-registry') statsTrackersRegistry;

  /** Args
    allocation = null;
    metric null; (one of 'cpu' or 'memory'
  */

  get metric() {
    assert('metric is a required argument', this.args.metric);
    return this.args.metric;
  }

  get tracker() {
    return this.statsTrackersRegistry.getTracker(this.args.allocation);
  }

  get data() {
    if (!this.tracker) return [];
    return get(this, `tracker.${this.metric}`);
  }

  get series() {
    const ret = this.tracker.tasks
      .map(task => ({
        name: task.task,
        data: task[this.metric],
      }))
      .reverse();

    return ret;
  }

  get reservedAmount() {
    return this.metric === 'cpu' ? this.tracker.reservedCPU : this.tracker.reservedMemory;
  }

  get chartClass() {
    if (this.metric === 'cpu') return 'is-info';
    if (this.metric === 'memory') return 'is-danger';
    return 'is-primary';
  }

  get colorScale() {
    if (this.metric === 'cpu') return 'blues';
    if (this.metric === 'memory') return 'reds';
    return 'ordinal';
  }

  @task(function*() {
    do {
      this.tracker.poll.perform();
      yield timeout(100);
    } while (!Ember.testing);
  })
  poller;

  @action
  start() {
    if (this.tracker) this.poller.perform();
  }

  willDestroy() {
    this.poller.cancelAll();
    this.tracker.signalPause.perform();
  }
}
<commit_msg>Memoize the tracker and the series properties to avoid repeat computation<commit_after>import Ember from 'ember';
import Component from '@glimmer/component';
import { task, timeout } from 'ember-concurrency';
import { assert } from '@ember/debug';
import { inject as service } from '@ember/service';
import { action, get, computed } from '@ember/object';

export default class AllocationPrimaryMetric extends Component {
  @service('stats-trackers-registry') statsTrackersRegistry;

  /** Args
    allocation = null;
    metric null; (one of 'cpu' or 'memory'
  */

  get metric() {
    assert('metric is a required argument', this.args.metric);
    return this.args.metric;
  }

  get allocation() {
    return this.args.allocation;
  }

  @computed('allocation')
  get tracker() {
    return this.statsTrackersRegistry.getTracker(this.allocation);
  }

  get data() {
    if (!this.tracker) return [];
    return get(this, `tracker.${this.metric}`);
  }

  @computed('tracker.tasks.[]', 'metric')
  get series() {
    const ret = this.tracker.tasks
      .map(task => ({
        name: task.task,
        data: task[this.metric],
      }))
      .reverse();

    return ret;
  }

  get reservedAmount() {
    return this.metric === 'cpu' ? this.tracker.reservedCPU : this.tracker.reservedMemory;
  }

  get chartClass() {
    if (this.metric === 'cpu') return 'is-info';
    if (this.metric === 'memory') return 'is-danger';
    return 'is-primary';
  }

  get colorScale() {
    if (this.metric === 'cpu') return 'blues';
    if (this.metric === 'memory') return 'reds';
    return 'ordinal';
  }

  @task(function*() {
    do {
      this.tracker.poll.perform();
      yield timeout(100);
    } while (!Ember.testing);
  })
  poller;

  @action
  start() {
    if (this.tracker) this.poller.perform();
  }

  willDestroy() {
    this.poller.cancelAll();
    this.tracker.signalPause.perform();
  }
}
<|endoftext|>


<commit_before>import _ from 'lodash'

export default ['$q', '$scope', '$http', '$stateParams', '$state', function($q, $scope, $http, $stateParams, $state) {

  // This must be predefined for ng-model
  // values as objects
  $scope.stageOptions = {
    metadata: {},
    current: {},
    saved: {}
  }

  // Default values for view state (form and section visibility)
  $scope.standardVariablesSectionShowing = false
  $scope.customVariablesSectionShowing = false
  $scope.createVarFormShowing = false
  $scope.editVarFormShowing = false
  $scope.editVarFormValues = {}
  $scope.editVarId = null

  /**
   * Opens the edit var form
   *
   * @param variable
   */
  $scope.editVar = function editVar(variable) {

    // Copy the values of the variable into the edit form
    $scope.editVarFormValues = {
      name: variable.name,
      description: variable.description,
      required: variable.required.toString(),
      default_value: variable.default_value
    }

    // Note which one we are editing
    $scope.editVarId = variable.id

    //
    $scope.editVarFormShowing = true

  }

  /**
   * Cancel making edits for a form var
   */
  $scope.cancelVarEdits = function cancelVarEdits() {
    $scope.editVarFormShowing = false
  }

  /**
   * Save edits to a variable (and reload variables)
   */
  $scope.saveVarEdits = () => {

    // If the var is required, make the default blank (since there shouldn't be a default value)
    if ($scope.editVarFormValues.required === '1') {
      $scope.editVarFormValues.default_value = ''
    }

    // Coerce the string required value into an integer
    $scope.editVarFormValues.required = parseInt($scope.editVarFormValues.required)

    // Send changes to server, then refresh variables
    console.log($scope.editVarFormValues)
    $http.patch('/api/pipelines/' + $stateParams.id + '/variables/' + $scope.editVarId, $scope.editVarFormValues)
      .then(() => {
        $scope.loadVariables()
      })

    // Hide the edit form
    $scope.editVarFormShowing = false
  }

  /**
   * Toggle the visibility of the standard variables section
   */
  $scope.toggleStandardSection = function toggleStandardSection() {
    $scope.standardVariablesSectionShowing = !$scope.standardVariablesSectionShowing
  }

  /**
   * Toggle visibility of the custom variables section
   */
  $scope.toggleCustomSection = function toggleCustomSection() {
    $scope.customVariablesSectionShowing = !$scope.customVariablesSectionShowing
  }

  /**
   * Show the create variable form
   */
  $scope.showCreateVarForm = function showCreateVarForm() {
    $scope.resetCreateVarForm()
    $scope.createVarFormShowing = true
  }

  /**
   * Reset the values in the create var form
   */
  $scope.resetCreateVarForm = function resetCreateVarForm() {
    $scope.createVarFormValues = {
      pipeline_config_id: $stateParams.id,
      name: '',
      description: '',
      required: '1',
      default_value: ''
    }
  }

  /**
   * "Cancel" var creation (closes the form)
   */
  $scope.cancelCreateVar = function cancelCreateVar() {
    $scope.createVarFormShowing = false
  }

  /**
   * Actual create of a new variable (typically triggered by the create var form)
   */
  $scope.createVar = function createVar() {

    $scope.createVarFormShowing = false

    // If the var is required, make the default blank (since there shouldn't be a default value)
    if ($scope.createVarFormValues.required === '1') {
      $scope.createVarFormValues.default_value = ''
    }

    // Create on server, then reset the create variable form and reload the variables data
    $http.post('/api/pipelines/' + $stateParams.id + '/variables', $scope.createVarFormValues).then(() => {
      $scope.resetCreateVarForm()
      $scope.loadVariables()
    })
  }

  /**
   * Load the pipeline and the related project
   */
  $scope.loadPipelinesAndProject = function loadPipelinesAndProject() {
    $http.get('/api/pipelines/' + $stateParams.id).then(response => {
      $scope.pipeline = response.data.data

      $http.get('/api/projects/' + response.data.data.project_id).then(response => {
        $scope.project = response.data.data
      })

    })
  }

  /**
   * Loads the pipeline variables
   */
  $scope.loadVariables = function loadVariables() {
    $http.get('/api/pipelines/' + $stateParams.id + '/variables').then(response => {
      $scope.variables = response.data.data
    })
  }

  /**
   * Loads Stage Data
   */
  $scope.loadStages = function loadStages() {

    let stageTypes = $http.get('/api/stage-types')
    let stageConfigs = $http.get('/api/pipelines/' + $stateParams.id + '/stages')
    $q.all([stageTypes, stageConfigs])
      .then(args => {
        let types = args[0].data.data
        let stages = args[1].data.data

        // Join the types with their stages
        $scope.stages = stages.map(stage => {
          let type = types.find(type => type.fqid === stage.type)
          if (type) {
            stage.schema = type
          }
          return stage
        })

        // Copy the options for stages to two maps
        stages.forEach(function(stage) {

          console.log(stage)

          // Set up holder objects
          // - one for the "last saved" value
          // - one for the current (bound) form value
          // - one for metadata
          $scope.stageOptions.current[stage.id] = {}
          $scope.stageOptions.saved[stage.id] = {}
          $scope.stageOptions.metadata[stage.id] = {}

          // For each option that exits in the stage type...
          Object.keys(stage.schema.options).forEach((key) => {

            let optionValue

            // If the user has specified value, use that
            // Otherwise, if there is a default value, use it
            // Otherwise, make the value blank
            if (typeof stage.options[key] !== 'undefined') {
              optionValue = stage.options[key].toString()

            } else if (typeof stage.schema.options[key].default !== 'undefined') {
              optionValue = stage.schema.options[key].default.toString()

            } else {
              optionValue = ''
            }

            $scope.stageOptions.current[stage.id][key] = optionValue
            $scope.stageOptions.saved[stage.id][key] = optionValue
            $scope.stageOptions.metadata[stage.id][key] = {
              name: stage.schema.options[key].name,
              description: stage.schema.options[key].description
            }

          })

        })

      })
  }

  /**
   * Save options for a stage
   *
   * @param stageId
   */
  $scope.saveOptions = function saveOptions(stageId) {

    // Save the options in the form to the cached "saved" values for comparison
    $scope.stageOptions.saved[stageId] = _.cloneDeep($scope.stageOptions.current[stageId])

    // Send the current values to the server
    $http.patch('/api/stage/' + stageId, {
      options: $scope.stageOptions.current[stageId]
    })

  }

  /**
   * Toggles the visibility of the options section for a stage
   *
   * @param {object} $event Angular event object, to operate on html element
   */
  $scope.toggleOptions = function toggleOptions($event) {
    $($event.target).parent().parent().find('.panel-body').slideToggle(200)
  }

  /**
   * Delete Stage (and reload stages)
   *
   * @param id
   */
  $scope.removeStage = (id) => {
    $http.delete('/api/stage/' + id)
    $scope.loadStages()
  }

  /**
   * Deletes a pipeline variable (and removes it from the scope array/obj locally)
   *
   * @param {object} variable Full variable object (typically from $scope.variables)
   */
  $scope.deleteVar = (variable) => {

    // Confirm
    if (confirm('Are you sure you want to delete variable "' + variable.name + '"?')) {

      // Delete on server
      $http.delete('/api/pipelines/' + $stateParams.id + '/variables/' + variable.id)

      // Delete locally
      $scope.variables.splice($scope.variables.indexOf(variable), 1)

    }

  }

  // Initialize view
  $scope.loadStages()
  $scope.loadPipelinesAndProject()
  $scope.loadVariables()

}]
<commit_msg>Formatting<commit_after>import _ from 'lodash'

export default ['$q', '$scope', '$http', '$stateParams', '$state', function($q, $scope, $http, $stateParams, $state) {

  // This must be predefined for ng-model
  // values as objects
  $scope.stageOptions = {
    metadata: {},
    current: {},
    saved: {}
  }

  // Default values for view state (form and section visibility)
  $scope.standardVariablesSectionShowing = false
  $scope.customVariablesSectionShowing = false
  $scope.createVarFormShowing = false
  $scope.editVarFormShowing = false
  $scope.editVarFormValues = {}
  $scope.editVarId = null

  /**
   * Opens the edit var form
   *
   * @param variable
   */
  $scope.editVar = function editVar(variable) {

    // Copy the values of the variable into the edit form
    $scope.editVarFormValues = {
      name: variable.name,
      description: variable.description,
      required: variable.required.toString(),
      default_value: variable.default_value
    }

    // Note which one we are editing
    $scope.editVarId = variable.id

    //
    $scope.editVarFormShowing = true

  }

  /**
   * Cancel making edits for a form var
   */
  $scope.cancelVarEdits = function cancelVarEdits() {
    $scope.editVarFormShowing = false
  }

  /**
   * Save edits to a variable (and reload variables)
   */
  $scope.saveVarEdits = () => {

    // If the var is required, make the default blank (since there shouldn't be a default value)
    if ($scope.editVarFormValues.required === '1') {
      $scope.editVarFormValues.default_value = ''
    }

    // Coerce the string required value into an integer
    $scope.editVarFormValues.required = parseInt($scope.editVarFormValues.required)

    // Send changes to server, then refresh variables
    console.log($scope.editVarFormValues)
    $http.patch('/api/pipelines/' + $stateParams.id + '/variables/' + $scope.editVarId, $scope.editVarFormValues)
      .then(() => {
        $scope.loadVariables()
      })

    // Hide the edit form
    $scope.editVarFormShowing = false
  }

  /**
   * Toggle the visibility of the standard variables section
   */
  $scope.toggleStandardSection = function toggleStandardSection() {
    $scope.standardVariablesSectionShowing = !$scope.standardVariablesSectionShowing
  }

  /**
   * Toggle visibility of the custom variables section
   */
  $scope.toggleCustomSection = function toggleCustomSection() {
    $scope.customVariablesSectionShowing = !$scope.customVariablesSectionShowing
  }

  /**
   * Show the create variable form
   */
  $scope.showCreateVarForm = function showCreateVarForm() {
    $scope.resetCreateVarForm()
    $scope.createVarFormShowing = true
  }

  /**
   * Reset the values in the create var form
   */
  $scope.resetCreateVarForm = function resetCreateVarForm() {
    $scope.createVarFormValues = {
      pipeline_config_id: $stateParams.id,
      name: '',
      description: '',
      required: '1',
      default_value: ''
    }
  }

  /**
   * "Cancel" var creation (closes the form)
   */
  $scope.cancelCreateVar = function cancelCreateVar() {
    $scope.createVarFormShowing = false
  }

  /**
   * Actual create of a new variable (typically triggered by the create var form)
   */
  $scope.createVar = function createVar() {

    $scope.createVarFormShowing = false

    // If the var is required, make the default blank (since there shouldn't be a default value)
    if ($scope.createVarFormValues.required === '1') {
      $scope.createVarFormValues.default_value = ''
    }

    // Create on server, then reset the create variable form and reload the variables data
    $http.post('/api/pipelines/' + $stateParams.id + '/variables', $scope.createVarFormValues).then(() => {
      $scope.resetCreateVarForm()
      $scope.loadVariables()
    })
  }

  /**
   * Load the pipeline and the related project
   */
  $scope.loadPipelinesAndProject = function loadPipelinesAndProject() {
    $http.get('/api/pipelines/' + $stateParams.id).then(response => {
      $scope.pipeline = response.data.data

      $http.get('/api/projects/' + response.data.data.project_id).then(response => {
        $scope.project = response.data.data
      })

    })
  }

  /**
   * Loads the pipeline variables
   */
  $scope.loadVariables = function loadVariables() {
    $http.get('/api/pipelines/' + $stateParams.id + '/variables').then(response => {
      $scope.variables = response.data.data
    })
  }

  /**
   * Loads Stage Data
   */
  $scope.loadStages = function loadStages() {

    let stageTypes = $http.get('/api/stage-types')
    let stageConfigs = $http.get('/api/pipelines/' + $stateParams.id + '/stages')
    $q.all([stageTypes, stageConfigs])
      .then(args => {
        let types = args[0].data.data
        let stages = args[1].data.data

        // Join the types with their stages
        $scope.stages = stages.map(stage => {
          let type = types.find(type => type.fqid === stage.type)
          if (type) {
            stage.schema = type
          }
          return stage
        })

        // Copy the options for stages to two maps
        stages.forEach(function(stage) {

          console.log(stage)

          // Set up holder objects
          // - one for the "last saved" value
          // - one for the current (bound) form value
          // - one for metadata
          $scope.stageOptions.current[stage.id] = {}
          $scope.stageOptions.saved[stage.id] = {}
          $scope.stageOptions.metadata[stage.id] = {}

          // For each option that exits in the stage type...
          Object.keys(stage.schema.options).forEach((key) => {

            let optionValue

            // If the user has specified value, use that
            // Otherwise, if there is a default value, use it
            // Otherwise, make the value blank
            if (typeof stage.options[key] !== 'undefined') {
              optionValue = stage.options[key].toString()

            } else if (typeof stage.schema.options[key].default !== 'undefined') {
              optionValue = stage.schema.options[key].default.toString()

            } else {
              optionValue = ''
            }

            $scope.stageOptions.current[stage.id][key] = optionValue
            $scope.stageOptions.saved[stage.id][key] = optionValue
            $scope.stageOptions.metadata[stage.id][key] = {
              name: stage.schema.options[key].name,
              description: stage.schema.options[key].description
            }

          })

        })

      })
  }

  /**
   * Save options for a stage
   *
   * @param stageId
   */
  $scope.saveOptions = function saveOptions(stageId) {

    // Save the options in the form to the cached "saved" values for comparison
    $scope.stageOptions.saved[stageId] = _.cloneDeep($scope.stageOptions.current[stageId])

    // Send the current values to the server
    $http.patch('/api/stage/' + stageId, {
      options: $scope.stageOptions.current[stageId]
    })

  }

  /**
   * Toggles the visibility of the options section for a stage
   *
   * @param {object} $event Angular event object, to operate on html element
   */
  $scope.toggleOptions = function toggleOptions($event) {
    $($event.target).parent().parent().find('.panel-body').slideToggle(200)
  }

  /**
   * Delete Stage (and reload stages)
   *
   * @param id
   */
  $scope.removeStage = (id) => {
    $http.delete('/api/stage/' + id)
    $scope.loadStages()
  }

  /**
   * Deletes a pipeline variable (and removes it from the scope array/obj locally)
   *
   * @param {object} variable Full variable object (typically from $scope.variables)
   */
  $scope.deleteVar = (variable) => {

    // Confirm
    if (confirm('Are you sure you want to delete variable "' + variable.name + '"?')) {

      // Delete on server
      $http.delete('/api/pipelines/' + $stateParams.id + '/variables/' + variable.id)

      // Delete locally
      $scope.variables.splice($scope.variables.indexOf(variable), 1)

    }

  }

  // Initialize view
  $scope.loadStages()
  $scope.loadPipelinesAndProject()
  $scope.loadVariables()

}]
<|endoftext|>


<commit_before>const $ = (q) => document.querySelector(q);
const saveToken = (v) => localStorage.setItem('token', v);
const loadToken = (v) => localStorage.getItem('token');
const removeToken = (v) => localStorage.removeItem('token');

(async ()=>
{

	if(location.href.match(/\?(clear|reset)/))
	{
		removeToken();
	}

	const API = "https://api.github.com"
	const REPO = 'notes';
	const PATH = 'posts'

	let instance = axios.create({
	  headers: {
	    Authorization: `token ${loadToken()}`
	  }
	})

	let user;
	const loading = $('#loading');
	try
	{
		const res = await instance.get(`${API}/user`);
		user = res.data;

	}
	catch(e)
	{
		// console.log('need log in');

		if(loading)
		{
			loading.classList.add('hide');
		}
		const tokenWrap = $('.token-wrap');
		const token = $('#token');
		const button = $('button');
		if(tokenWrap)
		{
			tokenWrap.classList.add('show');
			if(button)
			{

				button.onclick = ()=>
				{
					const {value} = token;
					if(value.length)
					{
						saveToken(value);
						console.log('token save');
						location.reload();
					}
				}
			}
		}
	}

	if(user)
	{
		// console.log(user);
		const searchParams = new URLSearchParams(document.location.search);
		const b64 = decodeURIComponent(searchParams.get('d'));
		const str = decodeURIComponent(escape(window.atob(b64)));
		const data = JSON.parse(str);

		const {
			link,
			author,
			image,
			time,
			title,
			video,
			tags,
			description,
			selection,
			filename
		} = data;

		const filepath = `${API}/repos/${user.login}/${REPO}/contents/${PATH}/${filename}.md`;

		let isExists;
		let sha;
		try{
			const res = await instance.get(filepath);
			isExists = true;
			sha = res.data.sha;
		}
		catch(e)
		{
			isExists = false;
		}

		//data to markdown
		let markdown = '---\n';
		[
			'link',
			'author',
			'image',
			'time',
			'title',
			'video',
			'tags',
		].forEach( k =>
		{
			if(data[k]) markdown += `${k}: ${data[k]}\n`
		});

		markdown += '---\n\n';

		if(title){
			markdown += `# ${title}\n\n`;
		}

		if(description){
			markdown += `> ${description}`;
		}

		markdown += `\n\n`;
		if(selection){
			const turndownService = new TurndownService()
			const md = turndownService.turndown(selection)
			markdown += `${md}\n\n`;
		}

		if(image){
			markdown += `![image](${image})\n\n`;
		}

		markdown += `${link}\n`;

		if(filename)
		{
			let res;
			try{

				res = await instance.put(
					filepath,
					{
						content: btoa(unescape(encodeURIComponent(markdown))),
						message: (isExists ? 'Create' : 'Update') + ` ${markdown}.md`,
						sha,
					}
				)
			}
			catch(e)
			{
				alert('can\'t create/update content');
				console.log(e);
				return;
			}

			window.history.back();
		}

	}

})()
<commit_msg>Update app.js<commit_after>const $ = (q) => document.querySelector(q);
const saveToken = (v) => localStorage.setItem('token', v);
const loadToken = (v) => localStorage.getItem('token');
const removeToken = (v) => localStorage.removeItem('token');

(async ()=>
{

	if(location.href.match(/\?(clear|reset)/))
	{
		removeToken();
	}

	const API = "https://api.github.com"
	const REPO = 'notes';
	const PATH = 'posts'

	let instance = axios.create({
	  headers: {
	    Authorization: `token ${loadToken()}`
	  }
	})

	let user;
	const loading = $('#loading');
	try
	{
		const res = await instance.get(`${API}/user`);
		user = res.data;

	}
	catch(e)
	{
		// console.log('need log in');

		if(loading)
		{
			loading.classList.add('hide');
		}
		const tokenWrap = $('.token-wrap');
		const token = $('#token');
		const button = $('button');
		if(tokenWrap)
		{
			tokenWrap.classList.add('show');
			if(button)
			{

				button.onclick = ()=>
				{
					const {value} = token;
					if(value.length)
					{
						saveToken(value);
						console.log('token save');
						location.reload();
					}
				}
			}
		}
	}

	if(user)
	{
		// console.log(user);
		const searchParams = new URLSearchParams(document.location.search);
		const b64 = decodeURIComponent(searchParams.get('d'));
		const str = decodeURIComponent(escape(window.atob(b64)));
		const data = JSON.parse(str);

		const {
			link,
			author,
			image,
			time,
			title,
			video,
			tags,
			description,
			selection,
			filename
		} = data;

		const filepath = `${API}/repos/${user.login}/${REPO}/contents/${PATH}/${filename}.md`;

		let isExists;
		let sha;
		try{
			const res = await instance.get(filepath);
			isExists = true;
			sha = res.data.sha;
		}
		catch(e)
		{
			isExists = false;
		}

		//data to markdown
		let markdown = '---\n';
		[
			'link',
			'author',
			'image',
			'time',
			'title',
			'video',
			'tags',
		].forEach( k =>
		{
			if(data[k]) markdown += `${k}: ${data[k]}\n`
		});

		markdown += '---\n\n';

		if(title){
			markdown += `# ${title}\n\n`;
		}

		if(description){
			markdown += `> ${description}`;
		}

		markdown += `\n\n`;
		if(selection){
			const turndownService = new TurndownService()
			const md = turndownService.turndown(selection)
			markdown += `${md}\n\n`;
		}

		if(image){
			markdown += `![image](${image})\n\n`;
		}

		markdown += `${link}\n`;

		if(filename)
		{
			let res;
			try{

				res = await instance.put(
					filepath,
					{
						content: btoa(unescape(encodeURIComponent(markdown))),
						message: (isExists ? 'Update' : 'Create') + ` ${filename}.md`,
						sha,
					}
				)
			}
			catch(e)
			{
				alert('can\'t create/update content');
				console.log(e);
				return;
			}

			window.history.back();
		}

	}

})()
<|endoftext|>


<commit_before>'use strict';

var db = require('../db');

exports._parent = require('./abstract-user-base');

exports['submitted-menu'] = function () {
	li({ id: 'users-admin-nav', class: 'submitted-menu-item-active' }, a({ href: '/' },
		db.Role.meta[this.user.currentRoleResolved].label));
};
<commit_msg>#1600 hard coded usersAdmin role in view<commit_after>'use strict';

var db = require('../db');

exports._parent = require('./abstract-user-base');

exports['submitted-menu'] = function () {
	li({ id: 'users-admin-nav', class: 'submitted-menu-item-active' }, a({ href: '/' },
		db.Role.meta.usersAdmin.label));
};
<|endoftext|>


<commit_before>var assert = require('chai').assert,
    sinon  = require('sinon'),
    events = require('events'),
    Router = require('../script/Router');

suite('Router', function () {
    setup(function () {
        this.connection = new events.EventEmitter;
        sinon.spy(this.connection, 'on');

        sinon.spy(Router.prototype, 'route');
        this.router = new Router({ incoming: this.connection });
    });

    teardown(function () {
        this.connection.on.restore();
        Router.prototype.route.restore();
    });

    test('recieves an incoming connection', function () {
        assert.equal(this.router.incoming, this.connection);
    });

    test('listens on incoming connection data', function () {
        assert.ok(this.connection.on.calledWith('data'));
    });

    test('attempts to route any incoming data', function () {
        this.connection.emit('data', 'mock');
        assert.ok(Router.prototype.route.calledWith('mock'));
    });
});
<commit_msg>Updated test spies to use prototype<commit_after>var assert = require('chai').assert,
    sinon  = require('sinon'),
    EventEmitter = require('events').EventEmitter,
    Router = require('../script/Router');

suite('Router', function () {
    setup(function () {
        sinon.spy(EventEmitter.prototype, 'on');
        this.connection = new EventEmitter;

        sinon.spy(Router.prototype, 'route');
        this.router = new Router({ incoming: this.connection });
    });

    teardown(function () {
        this.connection.on.restore();
        Router.prototype.route.restore();
    });

    test('recieves an incoming connection', function () {
        assert.equal(this.router.incoming, this.connection);
    });

    test('listens on incoming connection data', function () {
        assert.ok(EventEmitter.prototype.on.calledWith('data'));
    });

    test('attempts to route any incoming data', function () {
        this.connection.emit('data', 'mock');
        assert.ok(Router.prototype.route.calledWith('mock'));
    });
});
<|endoftext|>


<commit_before>var BayesianTesting = function() {

    /**
     * The propability B beats A in a binary-outcome test. Based on
     * http://www.evanmiller.org/bayesian-ab-testing.html
     *
     * @param  {int} alphaA number of success for A
     * @param  {int} betaA  number of failures for A
     * @param  {int} alphaB number of success for B
     * @param  {int} betaB  number of failures for B
     * @return {float}
     */
    this.propability_B_beats_A = function(alphaA, betaA, alphaB, betaB) {
        alphaA += 1;
        alphaB += 1;
        betaA += 1;
        betaB += 1;

        var total = 0.0;

        for (var i = 0; i < alphaB-1; i++) {
            total += Math.exp(lbeta(alphaA+i, betaB+betaA) - Math.log(betaB+i)
                - lbeta(1+i, betaB) - lbeta(alphaA, betaA))
        }

        return total;
    }

    /**
     * The propability C beats A and B in a binary-outcome test.
     *
     * @param  {int} alphaA number of success for A
     * @param  {int} betaA  number of failures for A
     * @param  {int} alphaB number of success for B
     * @param  {int} betaB  number of failures for B
     * @param  {int} alphaC number of success for C
     * @param  {int} betaC  number of failures for C
     * @return {float}
     */
    this.propability_C_beats_A_and_B = function(alphaA, betaA, alphaB, betaB, alphaC, betaC) {
        alphaA += 1;
        alphaB += 1;
        alphaC += 1;
        betaA += 1;
        betaB += 1;
        betaC += 1;

        var total = 0.0;

        for(var i = 0; i < alphaA-1; i++) {
            for(var j = 0; j < alphaB-1; j++) {
                total += Math.exp(lbeta(alphaC+i+j, betaA+betaB+betaC) -
                    Math.log(betaA+i) - Math.log(betaB+j) -
                    lbeta(1+i, betaA) - lbeta(1+j, betaB) - lbeta(alphaC, betaC))
            }
        }

        return 1 - this.propability_B_beats_A(alphaC-1,betaC-1,alphaA-1,betaA-1)
                 - this.propability_B_beats_A(alphaC-1,betaC-1,alphaB-1,betaB-1)
                 + total;
    }

    var lgamma = function(x) {
        var j = 0;
        var cof = [
            76.18009172947146, -86.50532032941677, 24.01409824083091,
            -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
        ];
        var ser = 1.000000000190015;
        var xx, y, tmp;
        tmp = (y = xx = x) + 5.5;
        tmp -= (xx + 0.5) * Math.log(tmp);
        for (; j < 6; j++)
        ser += cof[j] / ++y;
        return Math.log(2.5066282746310005 * ser / xx) - tmp;
    }

    var lbeta = function(x,y) {
        return lgamma(x) + lgamma(y) - lgamma(x + y);
    }

}<commit_msg>added formulas for count data<commit_after>var BayesianTesting = function() {

    /**
     * The propability B beats A in a binary-outcome test
     * (eg. conversion rate). Based on
     * http://www.evanmiller.org/bayesian-ab-testing.html
     *
     * @param  {int} alphaA number of success for A
     * @param  {int} betaA  number of failures for A
     * @param  {int} alphaB number of success for B
     * @param  {int} betaB  number of failures for B
     * @return {float}
     */
    this.propability_B_beats_A = function(alphaA, betaA, alphaB, betaB) {
        alphaA += 1;
        alphaB += 1;
        betaA += 1;
        betaB += 1;

        var total = 0.0;

        for (var i = 0; i < alphaB-1; i++) {
            total += Math.exp(lbeta(alphaA+i, betaB+betaA) - Math.log(betaB+i)
                - lbeta(1+i, betaB) - lbeta(alphaA, betaA))
        }

        return total;
    }

    /**
     * The propability C beats A and B in a
     * binary-outcome test (eg. conversion rate).
     *
     * @param  {int} alphaA number of success for A
     * @param  {int} betaA  number of failures for A
     * @param  {int} alphaB number of success for B
     * @param  {int} betaB  number of failures for B
     * @param  {int} alphaC number of success for C
     * @param  {int} betaC  number of failures for C
     * @return {float}
     */
    this.propability_C_beats_A_and_B = function(alphaA, betaA, alphaB, betaB, alphaC, betaC) {
        alphaA += 1;
        alphaB += 1;
        alphaC += 1;
        betaA += 1;
        betaB += 1;
        betaC += 1;

        var total = 0.0;

        for(var i = 0; i < alphaA-1; i++) {
            for(var j = 0; j < alphaB-1; j++) {
                total += Math.exp(lbeta(alphaC+i+j, betaA+betaB+betaC) -
                    Math.log(betaA+i) - Math.log(betaB+j) -
                    lbeta(1+i, betaA) - lbeta(1+j, betaB) - lbeta(alphaC, betaC))
            }
        }

        return 1 - this.propability_B_beats_A(alphaC-1,betaC-1,alphaA-1,betaA-1)
                 - this.propability_B_beats_A(alphaC-1,betaC-1,alphaB-1,betaB-1)
                 + total;
    }

    /**
     * Function for analyzing count data. Returns the propability
     * that group 1 has a higher arrivale rate than group 2.
     * http://www.evanmiller.org/bayesian-ab-testing.html#count_ab
     *
     * @param  {int} alpha1 total events for group one
     * @param  {int} beta1  total exposure for group one
     * @param  {int} alpha2 total events for group two
     * @param  {int} beta2  total exposure for group two
     * @return {int}
     */
    this.propability_1_beats_2 = function(alpha1,beta1,alpha2,beta2) {
        var total = 0.0;

        for(var k = 0; k < alpha1-1; k++) {
            total += Math.exp(k * Math.log(beta1) + alpha2 * Math.log(beta2)
                - (k + alpha2) * Math.log(beta1 + beta2)
                - Math.log(k + beta2) - lbeta(k + 1, alpha2))
        }

        return total;
    }

    /**
     * Function for analyzing count data. Returns the propability
     * that group 1 has a higher arrivale rate than group 2 and group 3.
     * http://www.evanmiller.org/bayesian-ab-testing.html#count_ab
     *
     * @param  {int} alpha1 total events for group one
     * @param  {int} beta1  total exposure for group one
     * @param  {int} alpha2 total events for group two
     * @param  {int} beta2  total exposure for group two
     * @param  {int} alpha3 total events for group three
     * @param  {int} beta3  total exposure for group three
     * @return {int}
     */
    this.propability_1_beats_2_and_3 = function(alpha1,beta1,alpha2,beta2,alpha3,beta3) {
        var total = 0.0;

        for(var k = 0; k < alpha2-1; k++) {
            for(var l = 0; l < alpha3-1; l++) {
                total += Math.exp(alpha1 * Math.log(beta1) + k * Math.log(beta2) + 1 * Math.log(beta3)
                    - (k+l+beta1) * Math.log(beta1 + beta2 + beta3)
                    + lgamma(k+l+alpha1) - lgamma(k+1) - lgamma(l+1) - lgamma(alpha1))
            }
        }

        return 1 - this.propability_1_beats_2(alpha2,beta2,alpha1,beta1)
                 - this.propability_1_beats_2(alpha3,beta3,alpha1,beta1)
                 + total;
    }

    var lgamma = function(x) {
        var j = 0;
        var cof = [
            76.18009172947146, -86.50532032941677, 24.01409824083091,
            -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
        ];
        var ser = 1.000000000190015;
        var xx, y, tmp;
        tmp = (y = xx = x) + 5.5;
        tmp -= (xx + 0.5) * Math.log(tmp);
        for (; j < 6; j++)
        ser += cof[j] / ++y;
        return Math.log(2.5066282746310005 * ser / xx) - tmp;
    }

    var lbeta = function(x,y) {
        return lgamma(x) + lgamma(y) - lgamma(x + y);
    }

}

var bt = new BayesianTesting();
console.log(bt.propability_1_beats_2(10,100,10,50))
console.log(bt.propability_1_beats_2_and_3(10,100,10,150,5,75))<|endoftext|>


<commit_before>describe("Basic functions", function() {
  beforeEach(function() {

  });

  describe("ctor", function() {
    it("should return the constructor of the object", function() {
      var O = function() { this.foo = 42; };

      expect(L.ctor([])).toBe(Array);
      expect(L.ctor({})).toBe(Object);
      expect(L.ctor(new O())).toBe(O);
      expect(L.ctor(null)).toBe(null);
    });
  });

  describe("cat", function() {
    it("should return an empty array when given no args", function() {
      expect(L.cat()).toEqual([]);
    });

    it("should concatenate one array", function() {
      var a = [];
      var b = [1,2,3];

      expect(L.cat(a)).toEqual([]);
      expect(L.cat(b)).toEqual([1,2,3]);
    });

    it("should concatenate two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.cat(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.cat(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should concatenate two items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;

      expect(L.cat(a,b)).toEqual([1,2]);
    });

    it("should concatenate 3 or more arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var c = [7,8,9];
      var d = [0,0,0];

      expect(L.cat(a,b,c)).toEqual([1,2,3,4,5,6,7,8,9]);
      expect(L.cat(a,b,c,d)).toEqual([1,2,3,4,5,6,7,8,9,0,0,0]);
    });

    it("should concatenate three or more items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;
      var c = 3;
      var d = [4];

      expect(L.cat(a,b,c)).toEqual([1,2,3]);
      expect(L.cat(a,b,c,d)).toEqual([1,2,3,4]);
    });

    it("should throw an exception if the first arg is not an array", function() {
      expect(function() { L.cat(1,2); }).toThrow();
      expect(function() { L.cat(1,[2]); }).toThrow();

    });
  });

  describe("pour", function() {
    it("should pour two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.pour(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.pour(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should pour two items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;

      expect(L.pour(a,b)).toEqual([1,2]);
    });

    it("should pour 3 or more arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var c = [7,8,9];
      var d = [0,0,0];

      expect(L.pour(a,b,c)).toEqual([1,2,3,4,5,6,7,8,9]);
      expect(L.pour(a,b,c,d)).toEqual([1,2,3,4,5,6,7,8,9,0,0,0]);
    });

    it("should pour three or more items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;
      var c = 3;
      var d = [4];

      expect(L.pour(a,b,c)).toEqual([1,2,3]);
      expect(L.pour(a,b,c,d)).toEqual([1,2,3,4]);
    });

    it("should throw an exception if the first arg is not an array", function() {
      expect(function() { L.pour(1,2); }).toThrow();
      expect(function() { L.pour(1,[2]); }).toThrow();

    });
  });

  describe("into", function() {
    it("should into two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.into(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.into(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should throw an exception if either arg is not an array", function() {
      expect(function() { L.into(1,2); }).toThrow();
      expect(function() { L.into(1,[2]); }).toThrow();
      expect(function() { L.into(); }).toThrow();
      expect(function() { L.into([]); }).toThrow();
    });
  });

  describe("butLast", function() {
    it("should give all but the last element in an array", function() {
      var a = [1,2,3];

      expect(L.butLast(a)).toEqual([1,2]);
      expect(L.butLast([])).toEqual([]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.butLast(a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given an array", function() {
      expect(function() { L.butLast(1); }).toThrow();
    });
  });

  describe("cons", function() {
    it("should insert the first arg into the array given as the second arg", function() {
      var a = [1,2,3];
      var b = [0];

      expect(L.cons(0, a)).toEqual([0,1,2,3]);
      expect(L.cons(b, a)).toEqual([[0],1,2,3]);
      expect(L.cons(0, [])).toEqual([0]);
    });

    it("should create a pair if the second is not an array", function() {
      expect(L.cons(1, 2)).toEqual([1,2]);
      expect(L.cons([1], 2)).toEqual([[1],2]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.cons(0, a);

      expect(a).toEqual([1,2,3]);
    });
  });

  describe("interpose", function() {
    it("should inject the first arg in-between the elements of the second array arg", function() {
      var a = [1,2,3];
      var b = [1,2];
      var c = [1];

      expect(L.interpose(0, a)).toEqual([1,0,2,0,3]);
      expect(L.interpose(0, [1])).toEqual([1]);
      expect(L.interpose(0, b)).toEqual([1,0,2]);
      expect(L.interpose(0, c)).toEqual([1]);
    });

    it("should properly handle empty array arguments", function() {
      expect(L.interpose(0, [])).toEqual([]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.interpose(0, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given an array", function() {
      expect(function() { L.interpose(0,1); }).toThrow();
      expect(function() { L.interpose(0); }).toThrow();
    });
  });

  describe("interleave", function() {
    it("should weave the arrays together", function() {
      var a = [1,2,3];
      var b = [1,2];
      var c = ['a', 'b', 'c'];

      expect(L.interleave(a,b)).toEqual([1,1,2,2,3]);
      expect(L.interleave(a,a)).toEqual([1,1,2,2,3,3]);
      expect(L.interleave(c,a)).toEqual(['a',1,'b',2,'c',3]);
      expect(L.interleave(a,b,c)).toEqual([1,1,'a',2,2,'b',3,'c']);
    });

    it("should properly handle empty arguments", function() {
      expect(L.interleave([], [])).toEqual([]);
      expect(L.interleave([1,2,3], [])).toEqual([1,2,3]);
      expect(L.interleave([], [1,2,3])).toEqual([1,2,3]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.interleave(a, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given arrays", function() {
      expect(function() { L.interleave(0,1); }).toThrow();
      expect(function() { L.interleave(0); }).toThrow();
      expect(function() { L.interleave(); }).toThrow();
    });
  });

  describe("repeat", function() {
    it("should build an array of some size with the specified element in each slot", function() {
      expect(L.repeat(3,1)).toEqual([1,1,1]);
      expect(L.repeat(0)).toEqual([]);
      expect(L.repeat(0,1)).toEqual([]);
    });

    it("should throw an exception if not given a number as the first arg", function() {
      expect(function() { L.repeat(); }).toThrow();
    });
  });

  describe("second", function() {
    it("should return the second element of the array given, undefined if outside of the bounds", function() {
      var a = [1,2,3];

      expect(L.second(a)).toEqual(2);
      expect(L.second([1])).toEqual(undefined);
      expect(L.second([])).toEqual(undefined);
    });

    it("should throw an exception if not given a number as the first arg", function() {
      expect(function() { L.second(1); }).toThrow();
    });
  });

  describe("increasing / increasingOrEq / decreasing / decreasingOrEq", function() {
    it("should recognize if the arguments are monotonically increasing or decreasing", function() {
      var inc = [1,2,3];
      var incAlso = [1,2,3,3,4];
      var dec = [5,4,3,2,1];
      var decAlso = [5,4,3,3,2,1];

      expect(L.increasing.apply(null, inc)).toBeTruthy();
      expect(L.increasingOrEq.apply(null, incAlso)).toBeTruthy();
      expect(L.increasing.apply(null, dec)).toBeFalsy();
      expect(L.decreasing.apply(null, dec)).toBeTruthy();
      expect(L.decreasingOrEq.apply(null, decAlso)).toBeTruthy();
      expect(L.decreasing.apply(null, inc)).toBeFalsy();
    });
  });

  describe("meth", function() {
    it("should wrap a method as a function and allow the target as the first arg", function() {
      var str = L.meth(Array.prototype.toString);

      expect(str([1])).toEqual('1');
    });
  });

  describe("assoc", function() {
    it("should allow the placement of a value at any depth in an associative structure", function() {
      var obj = {a: {b: {c: 42, d: 108}}};
      var ary = ['a', ['b', ['c', 'd'], 'e']];

      expect(L.assoc(obj, ['a', 'b', 'c'], 9)).toEqual({a: {b: {c: 9, d: 108}}});
      expect(L.assoc(ary, [1, 1, 0], 9)).toEqual(['a', ['b', [9, 'd'], 'e']]);

      expect(L.assoc(obj, 'a', 9)).toEqual({a: 9});
      expect(L.assoc(ary, 1, 9)).toEqual(['a', 9]);
    });

    it("should not modify the original", function() {
      var obj = {a: {b: {c: 42, d: 108}}};
      var ary = ['a', ['b', ['c', 'd'], 'e']];
      var _   = L.assoc(obj, ['a', 'b', 'c'], 9);
      var __  = L.assoc(ary, [1, 1, 0], 9);

      expect(obj).toEqual({a: {b: {c: 42, d: 108}}});
      expect(ary).toEqual(['a', ['b', ['c', 'd'], 'e']]);
    });
  });

  describe("splitAt", function() {
    it("should bifurcate an array at a given index, returning an array of the parts", function() {
      var a = [1,2,3,4,5];

      expect(L.splitAt(2, a)).toEqual([[1,2], [3,4,5]]);
      expect(L.splitAt(0, a)).toEqual([[], [1,2,3,4,5]]);
      expect(L.splitAt(5, a)).toEqual([[1,2,3,4,5], []]);
      expect(L.splitAt(2, [])).toEqual([[], []]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3,4,5];
      var _ = L.splitAt(2, a);

      expect(a).toEqual([1,2,3,4,5]);
    });

    it("should throw an exception if not given a number as the first arg or an array as the second", function() {
      expect(function() { L.splitAt('a', []); }).toThrow();
      expect(function() { L.splitAt(1); }).toThrow();
      expect(function() { L.splitAt(); }).toThrow();
    });
  });

  describe("takeSkipping", function() {
    it("should take every nth element in an array", function() {
      var a = _.range(10);

      expect(L.takeSkipping(2, a)).toEqual([0, 2, 4, 6, 8]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.takeSkipping(2, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given a number as the first arg or an array as the second", function() {
      expect(function() { L.takeSkipping('a', []); }).toThrow();
      expect(function() { L.takeSkipping(1); }).toThrow();
      expect(function() { L.takeSkipping(); }).toThrow();
    });
  });
});
<commit_msg>More specs for cat<commit_after>describe("Basic functions", function() {
  beforeEach(function() {

  });

  describe("ctor", function() {
    it("should return the constructor of the object", function() {
      var O = function() { this.foo = 42; };

      expect(L.ctor([])).toBe(Array);
      expect(L.ctor({})).toBe(Object);
      expect(L.ctor(new O())).toBe(O);
      expect(L.ctor(null)).toBe(null);
    });
  });

  describe("cat", function() {
    it("should return an empty array when given no args", function() {
      expect(L.cat()).toEqual([]);
    });

    it("should concatenate one array", function() {
      var a = [];
      var b = [1,2,3];

      expect(L.cat(a)).toEqual([]);
      expect(L.cat(b)).toEqual([1,2,3]);
    });

    it("should concatenate two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.cat(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should concatenate any number of arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var c = [7,8,9];

      expect(L.cat(a,b,c)).toEqual([1,2,3,4,5,6,7,8,9]);
      expect(L.cat(a,b,c,a,b,c)).toEqual([1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.cat(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should concatenate two items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;

      expect(L.cat(a,b)).toEqual([1,2]);
    });

    it("should concatenate 3 or more arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var c = [7,8,9];
      var d = [0,0,0];

      expect(L.cat(a,b,c)).toEqual([1,2,3,4,5,6,7,8,9]);
      expect(L.cat(a,b,c,d)).toEqual([1,2,3,4,5,6,7,8,9,0,0,0]);
    });

    it("should concatenate three or more items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;
      var c = 3;
      var d = [4];

      expect(L.cat(a,b,c)).toEqual([1,2,3]);
      expect(L.cat(a,b,c,d)).toEqual([1,2,3,4]);
    });

    it("should throw an exception if the first arg is not an array", function() {
      expect(function() { L.cat(1,2); }).toThrow();
      expect(function() { L.cat(1,[2]); }).toThrow();

    });
  });

  describe("pour", function() {
    it("should pour two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.pour(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.pour(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should pour two items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;

      expect(L.pour(a,b)).toEqual([1,2]);
    });

    it("should pour 3 or more arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var c = [7,8,9];
      var d = [0,0,0];

      expect(L.pour(a,b,c)).toEqual([1,2,3,4,5,6,7,8,9]);
      expect(L.pour(a,b,c,d)).toEqual([1,2,3,4,5,6,7,8,9,0,0,0]);
    });

    it("should pour three or more items, as long as the first is an array", function() {
      var a = [1];
      var b = 2;
      var c = 3;
      var d = [4];

      expect(L.pour(a,b,c)).toEqual([1,2,3]);
      expect(L.pour(a,b,c,d)).toEqual([1,2,3,4]);
    });

    it("should throw an exception if the first arg is not an array", function() {
      expect(function() { L.pour(1,2); }).toThrow();
      expect(function() { L.pour(1,[2]); }).toThrow();

    });
  });

  describe("into", function() {
    it("should into two arrays", function() {
      var a = [1,2,3];
      var b = [4,5,6];

      expect(L.into(a,b)).toEqual([1,2,3,4,5,6]);
    });

    it("should not modify the originals", function() {
      var a = [1,2,3];
      var b = [4,5,6];
      var _ = L.into(a,b);

      expect(a).toEqual([1,2,3]);
      expect(b).toEqual([4,5,6]);
    });

    it("should throw an exception if either arg is not an array", function() {
      expect(function() { L.into(1,2); }).toThrow();
      expect(function() { L.into(1,[2]); }).toThrow();
      expect(function() { L.into(); }).toThrow();
      expect(function() { L.into([]); }).toThrow();
    });
  });

  describe("butLast", function() {
    it("should give all but the last element in an array", function() {
      var a = [1,2,3];

      expect(L.butLast(a)).toEqual([1,2]);
      expect(L.butLast([])).toEqual([]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.butLast(a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given an array", function() {
      expect(function() { L.butLast(1); }).toThrow();
    });
  });

  describe("cons", function() {
    it("should insert the first arg into the array given as the second arg", function() {
      var a = [1,2,3];
      var b = [0];

      expect(L.cons(0, a)).toEqual([0,1,2,3]);
      expect(L.cons(b, a)).toEqual([[0],1,2,3]);
      expect(L.cons(0, [])).toEqual([0]);
    });

    it("should create a pair if the second is not an array", function() {
      expect(L.cons(1, 2)).toEqual([1,2]);
      expect(L.cons([1], 2)).toEqual([[1],2]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.cons(0, a);

      expect(a).toEqual([1,2,3]);
    });
  });

  describe("interpose", function() {
    it("should inject the first arg in-between the elements of the second array arg", function() {
      var a = [1,2,3];
      var b = [1,2];
      var c = [1];

      expect(L.interpose(0, a)).toEqual([1,0,2,0,3]);
      expect(L.interpose(0, [1])).toEqual([1]);
      expect(L.interpose(0, b)).toEqual([1,0,2]);
      expect(L.interpose(0, c)).toEqual([1]);
    });

    it("should properly handle empty array arguments", function() {
      expect(L.interpose(0, [])).toEqual([]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.interpose(0, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given an array", function() {
      expect(function() { L.interpose(0,1); }).toThrow();
      expect(function() { L.interpose(0); }).toThrow();
    });
  });

  describe("interleave", function() {
    it("should weave the arrays together", function() {
      var a = [1,2,3];
      var b = [1,2];
      var c = ['a', 'b', 'c'];

      expect(L.interleave(a,b)).toEqual([1,1,2,2,3]);
      expect(L.interleave(a,a)).toEqual([1,1,2,2,3,3]);
      expect(L.interleave(c,a)).toEqual(['a',1,'b',2,'c',3]);
      expect(L.interleave(a,b,c)).toEqual([1,1,'a',2,2,'b',3,'c']);
    });

    it("should properly handle empty arguments", function() {
      expect(L.interleave([], [])).toEqual([]);
      expect(L.interleave([1,2,3], [])).toEqual([1,2,3]);
      expect(L.interleave([], [1,2,3])).toEqual([1,2,3]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.interleave(a, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given arrays", function() {
      expect(function() { L.interleave(0,1); }).toThrow();
      expect(function() { L.interleave(0); }).toThrow();
      expect(function() { L.interleave(); }).toThrow();
    });
  });

  describe("repeat", function() {
    it("should build an array of some size with the specified element in each slot", function() {
      expect(L.repeat(3,1)).toEqual([1,1,1]);
      expect(L.repeat(0)).toEqual([]);
      expect(L.repeat(0,1)).toEqual([]);
    });

    it("should throw an exception if not given a number as the first arg", function() {
      expect(function() { L.repeat(); }).toThrow();
    });
  });

  describe("second", function() {
    it("should return the second element of the array given, undefined if outside of the bounds", function() {
      var a = [1,2,3];

      expect(L.second(a)).toEqual(2);
      expect(L.second([1])).toEqual(undefined);
      expect(L.second([])).toEqual(undefined);
    });

    it("should throw an exception if not given a number as the first arg", function() {
      expect(function() { L.second(1); }).toThrow();
    });
  });

  describe("increasing / increasingOrEq / decreasing / decreasingOrEq", function() {
    it("should recognize if the arguments are monotonically increasing or decreasing", function() {
      var inc = [1,2,3];
      var incAlso = [1,2,3,3,4];
      var dec = [5,4,3,2,1];
      var decAlso = [5,4,3,3,2,1];

      expect(L.increasing.apply(null, inc)).toBeTruthy();
      expect(L.increasingOrEq.apply(null, incAlso)).toBeTruthy();
      expect(L.increasing.apply(null, dec)).toBeFalsy();
      expect(L.decreasing.apply(null, dec)).toBeTruthy();
      expect(L.decreasingOrEq.apply(null, decAlso)).toBeTruthy();
      expect(L.decreasing.apply(null, inc)).toBeFalsy();
    });
  });

  describe("meth", function() {
    it("should wrap a method as a function and allow the target as the first arg", function() {
      var str = L.meth(Array.prototype.toString);

      expect(str([1])).toEqual('1');
    });
  });

  describe("assoc", function() {
    it("should allow the placement of a value at any depth in an associative structure", function() {
      var obj = {a: {b: {c: 42, d: 108}}};
      var ary = ['a', ['b', ['c', 'd'], 'e']];

      expect(L.assoc(obj, ['a', 'b', 'c'], 9)).toEqual({a: {b: {c: 9, d: 108}}});
      expect(L.assoc(ary, [1, 1, 0], 9)).toEqual(['a', ['b', [9, 'd'], 'e']]);

      expect(L.assoc(obj, 'a', 9)).toEqual({a: 9});
      expect(L.assoc(ary, 1, 9)).toEqual(['a', 9]);
    });

    it("should not modify the original", function() {
      var obj = {a: {b: {c: 42, d: 108}}};
      var ary = ['a', ['b', ['c', 'd'], 'e']];
      var _   = L.assoc(obj, ['a', 'b', 'c'], 9);
      var __  = L.assoc(ary, [1, 1, 0], 9);

      expect(obj).toEqual({a: {b: {c: 42, d: 108}}});
      expect(ary).toEqual(['a', ['b', ['c', 'd'], 'e']]);
    });
  });

  describe("splitAt", function() {
    it("should bifurcate an array at a given index, returning an array of the parts", function() {
      var a = [1,2,3,4,5];

      expect(L.splitAt(2, a)).toEqual([[1,2], [3,4,5]]);
      expect(L.splitAt(0, a)).toEqual([[], [1,2,3,4,5]]);
      expect(L.splitAt(5, a)).toEqual([[1,2,3,4,5], []]);
      expect(L.splitAt(2, [])).toEqual([[], []]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3,4,5];
      var _ = L.splitAt(2, a);

      expect(a).toEqual([1,2,3,4,5]);
    });

    it("should throw an exception if not given a number as the first arg or an array as the second", function() {
      expect(function() { L.splitAt('a', []); }).toThrow();
      expect(function() { L.splitAt(1); }).toThrow();
      expect(function() { L.splitAt(); }).toThrow();
    });
  });

  describe("takeSkipping", function() {
    it("should take every nth element in an array", function() {
      var a = _.range(10);

      expect(L.takeSkipping(2, a)).toEqual([0, 2, 4, 6, 8]);
    });

    it("should not modify the original", function() {
      var a = [1,2,3];
      var _ = L.takeSkipping(2, a);

      expect(a).toEqual([1,2,3]);
    });

    it("should throw an exception if not given a number as the first arg or an array as the second", function() {
      expect(function() { L.takeSkipping('a', []); }).toThrow();
      expect(function() { L.takeSkipping(1); }).toThrow();
      expect(function() { L.takeSkipping(); }).toThrow();
    });
  });
});
<|endoftext|>